Merge "Removed global windowFrames from PWM"
diff --git a/Android.bp b/Android.bp
index ca1c614..415eff3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,6 +368,7 @@
         "core/java/com/android/internal/app/IAppOpsService.aidl",
         "core/java/com/android/internal/app/IBatteryStats.aidl",
         "core/java/com/android/internal/app/ISoundTriggerService.aidl",
+        "core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl",
         "core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
         "core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
         "core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
@@ -1166,7 +1167,7 @@
     previous_api: ":last-released-public-api",
     merge_annotations_dirs: [
         "metalava-manual",
-        "ojluni-annotated-stubs",
+        "ojluni-annotated-sdk-stubs",
     ],
 }
 
@@ -1228,7 +1229,12 @@
     previous_api: ":last-released-public-api",
     merge_annotations_dirs: [
         "metalava-manual",
-        "ojluni-annotated-stubs",
+        "ojluni-annotated-sdk-stubs",
+    ],
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
     ],
 }
 
@@ -1499,7 +1505,7 @@
     previous_api: ":last-released-public-api",
     merge_annotations_dirs: [
         "metalava-manual",
-        "ojluni-annotated-stubs",
+        "ojluni-annotated-sdk-stubs",
     ],
     args: " --show-annotation android.annotation.SystemApi",
 }
@@ -1585,6 +1591,7 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -1609,6 +1616,7 @@
             removed_api_file: "api/system-removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
diff --git a/Android.mk b/Android.mk
index 5c4c237..d333074 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,265 +53,16 @@
 	cat $^ | sort -u > $@.tmp
 	$(call commit-change-for-toc,$@)
 
-# the documentation
-# ============================================================
-
-# TODO: deal with com/google/android/googleapps
-packages_to_document := \
-  android \
-  javax/microedition/khronos \
-  org/apache/http/conn \
-  org/apache/http/params \
-
-# include definition of libcore_to_document
-include libcore/Docs.mk
-
-non_base_dirs := \
-  ../opt/telephony/src/java/android/telephony \
-  ../opt/telephony/src/java/android/telephony/gsm \
-  ../opt/net/voip/src/java/android/net/rtp \
-  ../opt/net/voip/src/java/android/net/sip \
-
-# Find all files in specific directories (relative to frameworks/base)
-# to document and check apis
-files_to_check_apis := \
-  $(call find-other-java-files, \
-    $(non_base_dirs) \
-  )
-
-# Find all files in specific packages that were used to compile
-# framework.jar to document and check apis
-files_to_check_apis += \
-  $(addprefix ../../,\
-    $(filter \
-      $(foreach dir,$(FRAMEWORKS_BASE_JAVA_SRC_DIRS),\
-        $(foreach package,$(packages_to_document),\
-          $(dir)/$(package)/%.java)),\
-      $(SOONG_FRAMEWORK_SRCS)))
-
-# Find all generated files that were used to compile framework.jar
-files_to_check_apis_generated := \
-  $(filter $(OUT_DIR)/%,\
-    $(SOONG_FRAMEWORK_SRCS))
-
-# These are relative to frameworks/base
-# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-files_to_document := \
-  $(files_to_check_apis) \
-  $(call find-other-java-files,\
-    test-base/src \
-    test-mock/src \
-    test-runner/src)
-
-# These are relative to frameworks/base
-html_dirs := \
-	$(FRAMEWORKS_BASE_SUBDIRS) \
-	$(non_base_dirs) \
-
-# Common sources for doc check and api check
-common_src_files := \
-	$(call find-other-html-files, $(html_dirs)) \
-	$(addprefix ../../, $(libcore_to_document)) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_SRC_FILES := \
-  $(files_to_document) \
-  $(common_src_files) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_API_CHECK_SRC_FILES := \
-  $(files_to_check_apis) \
-  $(common_src_files) \
-
 # This is used by ide.mk as the list of source files that are
 # always included.
 INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
 
-framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \
-	$(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
-
-framework_docs_LOCAL_SRCJARS := $(SOONG_FRAMEWORK_SRCJARS)
-
-framework_docs_LOCAL_GENERATED_SOURCES := \
-  $(libcore_to_document_generated) \
-  $(files_to_check_apis_generated) \
-
-framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
-	core-oj \
-	core-libart \
-	conscrypt \
-	bouncycastle \
-	okhttp \
-	ext \
-	framework \
-	voip-common \
-
-# Platform docs can refer to Support Library APIs, but we don't actually build
-# them as part of the docs target, so we need to include them on the classpath.
-framework_docs_LOCAL_JAVA_LIBRARIES := \
-	$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) \
-	$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES)
-
-framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
-# The since flag (-since N.xml API_LEVEL) is used to add API Level information
-# to the reference documentation. Must be in order of oldest to newest.
-#
-# Conscrypt (com.android.org.conscrypt) is an implementation detail and should
-# not be referenced in the documentation.
-framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-    -android \
-    -knowntags ./frameworks/base/docs/knowntags.txt \
-    -knowntags ./libcore/known_oj_tags.txt \
-    -manifest ./frameworks/base/core/res/AndroidManifest.xml \
-    -hidePackage com.android.internal \
-    -hidePackage com.android.internal.util \
-    -hidePackage com.android.okhttp \
-    -hidePackage com.android.org.conscrypt \
-    -hidePackage com.android.server
-
-# Convert an sdk level to a "since" argument.
-since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.$(2)) $(1)
-
-finalized_xml_sdks := $(call numerically_sort,\
-    $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
-    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml)))
-finalized_txt_sdks := $(call numerically_sort,\
-     $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
-    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt)))
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_xml_sdks),$(call since-arg,$(sdk),xml))
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_txt_sdks),$(call since-arg,$(sdk),txt))
-ifneq ($(PLATFORM_VERSION_CODENAME),REL)
-  framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-      -since ./frameworks/base/api/current.txt $(PLATFORM_VERSION_CODENAME)
-endif
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-    -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-    -overview $(LOCAL_PATH)/core/java/overview.html
-
-framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
-
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
-	$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-
-framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
-    frameworks/base/docs/knowntags.txt \
-    $(libcore_to_document_generated)
-
-samples_dir := development/samples/browseable
-
-# Whitelist of valid groups, used for default TOC grouping. Each sample must
-# belong to one (and only one) group. Assign samples to groups by setting
-# a sample.group var to one of these groups in the sample's _index.jd.
-sample_groups := -samplegroup Admin \
-                 -samplegroup Background \
-                 -samplegroup Connectivity \
-                 -samplegroup Content \
-                 -samplegroup Input \
-                 -samplegroup Media \
-                 -samplegroup Notification \
-                 -samplegroup RenderScript \
-                 -samplegroup Security \
-                 -samplegroup Sensors \
-                 -samplegroup System \
-                 -samplegroup Testing \
-                 -samplegroup UI \
-                 -samplegroup Views \
-                 -samplegroup Wearable
-
-## SDK version identifiers used in the published docs
-  # major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=7.0
-  # release version (ie "Release x")  (full releases only)
-framework_docs_SDK_REL_ID:=1
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-		-hdf dac true \
-		-hdf sdk.codename O \
-		-hdf sdk.preview.version 1 \
-		-hdf sdk.version $(framework_docs_SDK_VERSION) \
-		-hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
-		-hdf sdk.preview 0 \
-		-resourcesdir $(LOCAL_PATH)/docs/html/reference/images/ \
-		-resourcesoutdir reference/android/images/
-
-# Federate Support Library references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-		-federate SupportLib https://developer.android.com \
-		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
-
-# Federate AndroidX references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-		-federate AndroidX https://developer.android.com \
-		-federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
-
-# Get the highest numbered api txt for the given api level.
-# $(1): the api level (e.g. public, system)
-define highest_sdk_txt
-$(HISTORICAL_SDK_VERSIONS_ROOT)/$(lastword $(call numerically_sort, \
-    $(patsubst \
-        $(HISTORICAL_SDK_VERSIONS_ROOT)/%,\
-        %,\
-        $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/$(1)/api/android.txt)\
-    ) \
-))
-endef
-
-# ====  Public API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
-	$(INTERNAL_PLATFORM_API_FILE)
-
-LOCAL_MODULE := offline-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,public))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
-# ====  System API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
-	$(INTERNAL_PLATFORM_SYSTEM_API_FILE)
-
-LOCAL_MODULE := offline-system-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,system))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
 
 # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
 # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
diff --git a/api/current.txt b/api/current.txt
index 1eecc2d..6042b2f 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5581,14 +5581,14 @@
     ctor public Notification.WearableExtender(android.app.Notification);
     method public android.app.Notification.WearableExtender addAction(android.app.Notification.Action);
     method public android.app.Notification.WearableExtender addActions(java.util.List<android.app.Notification.Action>);
-    method public android.app.Notification.WearableExtender addPage(android.app.Notification);
-    method public android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public deprecated android.app.Notification.WearableExtender addPage(android.app.Notification);
+    method public deprecated android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
     method public android.app.Notification.WearableExtender clearActions();
-    method public android.app.Notification.WearableExtender clearPages();
+    method public deprecated android.app.Notification.WearableExtender clearPages();
     method public android.app.Notification.WearableExtender clone();
     method public android.app.Notification.Builder extend(android.app.Notification.Builder);
     method public java.util.List<android.app.Notification.Action> getActions();
-    method public android.graphics.Bitmap getBackground();
+    method public deprecated android.graphics.Bitmap getBackground();
     method public java.lang.String getBridgeTag();
     method public int getContentAction();
     method public deprecated int getContentIcon();
@@ -5597,17 +5597,17 @@
     method public deprecated int getCustomContentHeight();
     method public deprecated int getCustomSizePreset();
     method public java.lang.String getDismissalId();
-    method public android.app.PendingIntent getDisplayIntent();
+    method public deprecated android.app.PendingIntent getDisplayIntent();
     method public deprecated int getGravity();
-    method public boolean getHintAmbientBigPicture();
+    method public deprecated boolean getHintAmbientBigPicture();
     method public deprecated boolean getHintAvoidBackgroundClipping();
     method public boolean getHintContentIntentLaunchesActivity();
     method public deprecated boolean getHintHideIcon();
     method public deprecated int getHintScreenTimeout();
     method public deprecated boolean getHintShowBackgroundOnly();
-    method public java.util.List<android.app.Notification> getPages();
+    method public deprecated java.util.List<android.app.Notification> getPages();
     method public boolean getStartScrollBottom();
-    method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+    method public deprecated android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
     method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
     method public android.app.Notification.WearableExtender setContentAction(int);
     method public deprecated android.app.Notification.WearableExtender setContentIcon(int);
@@ -5616,23 +5616,23 @@
     method public deprecated android.app.Notification.WearableExtender setCustomContentHeight(int);
     method public deprecated android.app.Notification.WearableExtender setCustomSizePreset(int);
     method public android.app.Notification.WearableExtender setDismissalId(java.lang.String);
-    method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public deprecated android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
     method public deprecated android.app.Notification.WearableExtender setGravity(int);
-    method public android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
+    method public deprecated android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
     method public deprecated android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
     method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public deprecated android.app.Notification.WearableExtender setHintHideIcon(boolean);
     method public deprecated android.app.Notification.WearableExtender setHintScreenTimeout(int);
     method public deprecated android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
     method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
-    field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
-    field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
-    field public static final int SIZE_DEFAULT = 0; // 0x0
-    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
-    field public static final int SIZE_LARGE = 4; // 0x4
-    field public static final int SIZE_MEDIUM = 3; // 0x3
-    field public static final int SIZE_SMALL = 2; // 0x2
-    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final deprecated int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field public static final deprecated int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field public static final deprecated int SIZE_DEFAULT = 0; // 0x0
+    field public static final deprecated int SIZE_FULL_SCREEN = 5; // 0x5
+    field public static final deprecated int SIZE_LARGE = 4; // 0x4
+    field public static final deprecated int SIZE_MEDIUM = 3; // 0x3
+    field public static final deprecated int SIZE_SMALL = 2; // 0x2
+    field public static final deprecated int SIZE_XSMALL = 1; // 0x1
     field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
   }
 
@@ -7734,7 +7734,9 @@
     method public boolean isMultipleAdvertisementSupported();
     method public boolean isOffloadedFilteringSupported();
     method public boolean isOffloadedScanBatchingSupported();
+    method public android.bluetooth.BluetoothServerSocket listenUsingInsecureL2capChannel() throws java.io.IOException;
     method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
+    method public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
     method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
     method public boolean setName(java.lang.String);
     method public boolean startDiscovery();
@@ -8102,7 +8104,9 @@
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
     method public boolean createBond();
+    method public android.bluetooth.BluetoothSocket createInsecureL2capChannel(int) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+    method public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public int describeContents();
     method public boolean fetchUuidsWithSdp();
@@ -8518,6 +8522,7 @@
     method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
     method public void close() throws java.io.IOException;
+    method public int getPsm();
   }
 
   public final class BluetoothSocket implements java.io.Closeable {
@@ -14010,12 +14015,15 @@
     method public android.graphics.Paint.Style getStyle();
     method public android.graphics.Paint.Align getTextAlign();
     method public void getTextBounds(java.lang.String, int, int, android.graphics.Rect);
+    method public void getTextBounds(java.lang.CharSequence, int, int, android.graphics.Rect);
     method public void getTextBounds(char[], int, int, android.graphics.Rect);
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
     method public void getTextPath(char[], int, int, float, float, android.graphics.Path);
     method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path);
     method public float getTextRunAdvances(char[], int, int, int, int, boolean, float[], int);
+    method public int getTextRunCursor(char[], int, int, boolean, int, int);
+    method public int getTextRunCursor(java.lang.CharSequence, int, int, boolean, int, int);
     method public float getTextScaleX();
     method public float getTextSize();
     method public float getTextSkewX();
@@ -14082,6 +14090,11 @@
     method public void setWordSpacing(float);
     method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
     field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
+    field public static final int CURSOR_AFTER = 0; // 0x0
+    field public static final int CURSOR_AT = 4; // 0x4
+    field public static final int CURSOR_AT_OR_AFTER = 1; // 0x1
+    field public static final int CURSOR_AT_OR_BEFORE = 3; // 0x3
+    field public static final int CURSOR_BEFORE = 2; // 0x2
     field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
     field public static final int DITHER_FLAG = 4; // 0x4
     field public static final int EMBEDDED_BITMAP_TEXT_FLAG = 1024; // 0x400
@@ -18677,6 +18690,37 @@
     field public static final int WORD_NUMBER_LIMIT = 200; // 0xc8
   }
 
+  public abstract class CaseMap {
+    method public static android.icu.text.CaseMap.Fold fold();
+    method public abstract android.icu.text.CaseMap omitUnchangedText();
+    method public static android.icu.text.CaseMap.Lower toLower();
+    method public static android.icu.text.CaseMap.Title toTitle();
+    method public static android.icu.text.CaseMap.Upper toUpper();
+  }
+
+  public static final class CaseMap.Fold extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Fold omitUnchangedText();
+    method public android.icu.text.CaseMap.Fold turkic();
+  }
+
+  public static final class CaseMap.Lower extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Lower omitUnchangedText();
+  }
+
+  public static final class CaseMap.Title extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Title noBreakAdjustment();
+    method public android.icu.text.CaseMap.Title noLowercase();
+    method public android.icu.text.CaseMap.Title omitUnchangedText();
+  }
+
+  public static final class CaseMap.Upper extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Upper omitUnchangedText();
+  }
+
   public final class CollationElementIterator {
     method public int getMaxExpansion(int);
     method public int getOffset();
@@ -19342,6 +19386,30 @@
     enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING;
   }
 
+  public final class Edits {
+    ctor public Edits();
+    method public void addReplace(int, int);
+    method public void addUnchanged(int);
+    method public android.icu.text.Edits.Iterator getCoarseChangesIterator();
+    method public android.icu.text.Edits.Iterator getCoarseIterator();
+    method public android.icu.text.Edits.Iterator getFineChangesIterator();
+    method public android.icu.text.Edits.Iterator getFineIterator();
+    method public boolean hasChanges();
+    method public int lengthDelta();
+    method public void reset();
+  }
+
+  public static final class Edits.Iterator {
+    method public int destinationIndex();
+    method public boolean findSourceIndex(int);
+    method public boolean hasChange();
+    method public int newLength();
+    method public boolean next();
+    method public int oldLength();
+    method public int replacementIndex();
+    method public int sourceIndex();
+  }
+
   public abstract class IDNA {
     method public static android.icu.text.IDNA getUTS46Instance(int);
     method public abstract java.lang.StringBuilder labelToASCII(java.lang.CharSequence, java.lang.StringBuilder, android.icu.text.IDNA.Info);
@@ -39860,6 +39928,7 @@
     method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
+    method public java.util.Set<java.lang.String> onGetSupportedVoiceActions(java.util.Set<java.lang.String>);
     method public void onLaunchVoiceAssistFromKeyguard();
     method public void onReady();
     method public void onShutdown();
@@ -49698,6 +49767,7 @@
     method public boolean isScrollable();
     method public boolean isSelected();
     method public boolean isShowingHintText();
+    method public boolean isTextEntryKey();
     method public boolean isVisibleToUser();
     method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
     method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
@@ -49759,6 +49829,7 @@
     method public void setSource(android.view.View);
     method public void setSource(android.view.View, int);
     method public void setText(java.lang.CharSequence);
+    method public void setTextEntryKey(boolean);
     method public void setTextSelection(int, int);
     method public void setTooltipText(java.lang.CharSequence);
     method public void setTraversalAfter(android.view.View);
diff --git a/api/system-current.txt b/api/system-current.txt
index 79bbace..8f7606a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -219,6 +219,14 @@
     field public static final int config_sendPackageName = 17891328; // 0x1110000
   }
 
+  public static final class R.dimen {
+    field public static final int config_restricted_icon_size = 17104903; // 0x1050007
+  }
+
+  public static final class R.drawable {
+    field public static final int ic_info = 17301684; // 0x10800b4
+  }
+
   public static final class R.raw {
     field public static final int loaderror = 17825792; // 0x1100000
     field public static final int nodomain = 17825793; // 0x1100001
@@ -357,11 +365,6 @@
     field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
   }
 
-  public final class AutomaticZenRule implements android.os.Parcelable {
-    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long);
-    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long, android.service.notification.ZenPolicy);
-  }
-
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
     method public void setDontSendToRestrictedApps(boolean);
@@ -1115,6 +1118,7 @@
   public class PackageItemInfo {
     method public deprecated java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager, float, int);
+    method public static void setForceSafeLabels(boolean);
     field public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
     field public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
     field public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
@@ -4195,6 +4199,7 @@
     method public final void attachBaseContext(android.content.Context);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract java.util.List<android.content.pm.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.permissionpresenterservice.RuntimePermissionPresenterService";
   }
 
@@ -5400,7 +5405,6 @@
   }
 
   public class TelephonyManager {
-    method public deprecated void answerRingingCall();
     method public deprecated void call(java.lang.String, java.lang.String);
     method public int checkCarrierPrivilegesForPackage(java.lang.String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
@@ -5408,7 +5412,6 @@
     method public boolean disableDataConnectivity();
     method public boolean enableDataConnectivity();
     method public void enableVideoCalling(boolean);
-    method public deprecated boolean endCall();
     method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     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);
@@ -6590,6 +6593,10 @@
 
 package android.view {
 
+  public abstract class Window {
+    method public void addPrivateFlags(int);
+  }
+
   public abstract interface WindowManager implements android.view.ViewManager {
     method public abstract android.graphics.Region getCurrentImeTouchRegion();
   }
@@ -6597,6 +6604,7 @@
   public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
     method public final long getUserActivityTimeout();
     method public final void setUserActivityTimeout(long);
+    field public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
   }
 
 }
diff --git a/api/system-removed.txt b/api/system-removed.txt
index b88c760..9012c33 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -143,3 +143,12 @@
 
 }
 
+package android.telephony {
+
+  public class TelephonyManager {
+    method public deprecated void answerRingingCall();
+    method public deprecated boolean endCall();
+  }
+
+}
+
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 36e51b9..12fb4a3 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -470,7 +470,8 @@
                     onExecute(provider);
                 } finally {
                     if (provider != null) {
-                        activityManager.removeContentProviderExternal(providerName, token);
+                        activityManager.removeContentProviderExternalAsUser(
+                                providerName, token, mUserId);
                     }
                 }
             } catch (Exception e) {
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 87799b3..cd48af9 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -407,7 +407,19 @@
 
 WorkerThreadSection::~WorkerThreadSection() {}
 
+void sigpipe_handler(int signum) {
+    if (signum == SIGPIPE) {
+        ALOGE("Wrote to a broken pipe\n");
+    } else {
+        ALOGE("Received unexpected signal: %d\n", signum);
+    }
+}
+
 static void* worker_thread_func(void* cookie) {
+    // Don't crash the service if we write to a closed pipe (which can happen if
+    // dumping times out).
+    signal(SIGPIPE, sigpipe_handler);
+
     WorkerThreadData* data = (WorkerThreadData*)cookie;
     status_t err = data->section->BlockingCall(data->pipe.writeFd().get());
 
@@ -486,6 +498,7 @@
             }
         }
     }
+
     write_section_stats(requests->sectionStats(this->id), buffer);
     if (timedOut || buffer.timedOut()) {
         ALOGW("[%s] timed out", this->name.string());
@@ -773,7 +786,10 @@
         }
     }
     gLastLogsRetrieved[mLogID] = lastTimestamp;
-    proto.flush(pipeWriteFd);
+    if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+        return EPIPE;
+    }
     return NO_ERROR;
 }
 
@@ -875,7 +891,7 @@
             break;
         }
         if (cStatus != NO_ERROR) {
-            ALOGE("TombstoneSection '%s' child had an issue: %s\n", this->name.string(), strerror(-cStatus));
+            ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
         }
 
         auto dump = std::make_unique<char[]>(buffer.size());
@@ -894,7 +910,13 @@
         dumpPipe.readFd().reset();
     }
 
-    proto.flush(pipeWriteFd);
+    if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+        if (err != NO_ERROR) {
+            return EPIPE;
+        }
+    }
+
     return err;
 }
 
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 7b6d29b..fc1a61c 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -147,6 +147,9 @@
         case STRING:
             str_value = from.str_value;
             break;
+        case STORAGE:
+            storage_value = from.storage_value;
+            break;
         default:
             break;
     }
@@ -164,6 +167,8 @@
             return std::to_string(double_value) + "[D]";
         case STRING:
             return str_value + "[S]";
+        case STORAGE:
+            return "bytes of size " + std::to_string(storage_value.size()) + "[ST]";
         default:
             return "[UNKNOWN]";
     }
@@ -183,6 +188,8 @@
             return double_value == that.double_value;
         case STRING:
             return str_value == that.str_value;
+        case STORAGE:
+            return storage_value == that.storage_value;
         default:
             return false;
     }
@@ -201,6 +208,8 @@
             return double_value != that.double_value;
         case STRING:
             return str_value != that.str_value;
+        case STORAGE:
+            return storage_value != that.storage_value;
         default:
             return false;
     }
@@ -220,6 +229,8 @@
             return double_value < that.double_value;
         case STRING:
             return str_value < that.str_value;
+        case STORAGE:
+            return storage_value < that.storage_value;
         default:
             return false;
     }
@@ -239,6 +250,8 @@
             return double_value > that.double_value;
         case STRING:
             return str_value > that.str_value;
+        case STORAGE:
+            return storage_value > that.storage_value;
         default:
             return false;
     }
@@ -258,6 +271,8 @@
             return double_value >= that.double_value;
         case STRING:
             return str_value >= that.str_value;
+        case STORAGE:
+            return storage_value >= that.storage_value;
         default:
             return false;
     }
@@ -274,6 +289,11 @@
         return v;
     }
 
+    if (type == STORAGE) {
+        ALOGE("Can't operate on storage value type");
+        return v;
+    }
+
     switch (type) {
         case INT:
             v.setInt(int_value - that.int_value);
@@ -311,6 +331,9 @@
         case STRING:
             str_value = that.str_value;
             break;
+        case STORAGE:
+            storage_value = that.storage_value;
+            break;
         default:
             break;
     }
@@ -326,6 +349,10 @@
         ALOGE("Can't operate on string value type");
         return *this;
     }
+    if (type == STORAGE) {
+        ALOGE("Can't operate on storage value type");
+        return *this;
+    }
 
     switch (type) {
         case INT:
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index b1b885e..77163f9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -32,7 +32,7 @@
 const int32_t kClearLastBitDeco = 0x7f;
 const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
 
-enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE };
 
 int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
 
@@ -293,6 +293,11 @@
         type = STRING;
     }
 
+    Value(const std::vector<uint8_t>& v) {
+        storage_value = v;
+        type = STORAGE;
+    }
+
     void setInt(int32_t v) {
         int_value = v;
         type = INT;
@@ -320,6 +325,7 @@
         double double_value;
     };
     std::string str_value;
+    std::vector<uint8_t> storage_value;
 
     Type type;
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 786e76c..b8f19ee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -133,6 +133,9 @@
         VibratorStateChanged vibrator_state_changed = 84;
         DeferredJobStatsReported deferred_job_stats_reported = 85;
         ThermalThrottlingStateChanged thermal_throttling = 86;
+        FingerprintAcquired fingerprint_acquired = 87;
+        FingerprintAuthenticated fingerprint_authenticated = 88;
+        FingerprintErrorOccurred fingerprint_error_occurred = 89;
     }
 
     // Pulled events will start at field 10000.
@@ -167,8 +170,9 @@
         DirectoryUsage directory_usage = 10026;
         AppSize app_size = 10027;
         CategorySize category_size = 10028;
-        android.service.procstats.ProcessStatsSectionProto proc_stats = 10029;
         BatteryVoltage battery_voltage = 10030;
+        NumFingerprints num_fingerprints = 10031;
+        ProcStats proc_stats = 10029;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -432,9 +436,9 @@
  * Logs reporting of a ble scan finding results.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
  */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+// TODO: Consider also tracking per-scanner-id.
 message BleScanResultReceived {
     repeated AttributionNode attribution_node = 1;
 
@@ -1832,6 +1836,60 @@
     optional android.os.statsd.EventType event_id = 2;
 }
 
+/**
+ * Logs when a fingerprint acquire event occurs.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintAcquired {
+    // The associated user. Eg: 0 for owners, 10+ for others.
+    // Defined in android/os/UserHandle.java
+    optional int32 user = 1;
+    // If this acquire is for a crypto fingerprint.
+    // e.g. Secure purchases, unlock password storage.
+    optional bool is_crypto = 2;
+}
+
+/**
+ * Logs when a fingerprint authentication event occurs.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintAuthenticated {
+    // The associated user. Eg: 0 for owners, 10+ for others.
+    // Defined in android/os/UserHandle.java
+    optional int32 user = 1;
+    // If this authentication is for a crypto fingerprint.
+    // e.g. Secure purchases, unlock password storage.
+    optional bool is_crypto = 2;
+    // Whether or not this authentication was successful.
+    optional bool is_authenticated = 3;
+}
+
+/**
+ * Logs when a fingerprint error occurs.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+ */
+message FingerprintErrorOccurred {
+    // The associated user. Eg: 0 for owners, 10+ for others.
+    // Defined in android/os/UserHandle.java
+    optional int32 user = 1;
+    // If this error is for a crypto fingerprint.
+    // e.g. Secure purchases, unlock password storage.
+    optional bool is_crypto = 2;
+
+    enum Error {
+        UNKNOWN = 0;
+        LOCKOUT = 1;
+        PERMANENT_LOCKOUT = 2;
+    }
+    // The type of error.
+    optional Error error = 3;
+}
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
@@ -2408,3 +2466,23 @@
     // Uses System.currentTimeMillis(), which is wall clock time.
     optional int64 cache_time_millis = 3;
 }
+
+/**
+ * Pulls the number of fingerprints for each user.
+ *
+ * Pulled from StatsCompanionService, which queries FingerprintManager.
+ */
+message NumFingerprints {
+    // The associated user. Eg: 0 for owners, 10+ for others.
+    // Defined in android/os/UserHandle.java
+    optional int32 user = 1;
+    // Number of fingerprints registered to that user.
+    optional int32 num_fingerprints = 2;
+}
+
+/**
+ * Pulled from ProcessStatsService.java
+ */
+message ProcStats {
+    optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
+}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index d953f50..6d7bba0 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -36,8 +36,6 @@
 namespace os {
 namespace statsd {
 
-const int kLogMsgHeaderSize = 28;
-
 // The reading and parsing are implemented in Java. It is not difficult to port over. But for now
 // let StatsCompanionService handle that and send the data back.
 StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) {
@@ -56,20 +54,12 @@
         vector<StatsLogEventWrapper> returned_value;
         Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
         if (!status.isOk()) {
-            ALOGW("error pulling for %d", mTagId);
+            ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
             return false;
         }
         data->clear();
-        int32_t timestampSec = getWallClockSec();
         for (const StatsLogEventWrapper& it : returned_value) {
-            log_msg tmp;
-            tmp.entry_v1.len = it.bytes.size();
-            // Manually set the header size to 28 bytes to match the pushed log events.
-            tmp.entry.hdr_size = kLogMsgHeaderSize;
-            tmp.entry_v1.sec = timestampSec;
-            // And set the received bytes starting after the 28 bytes reserved for header.
-            std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
-            data->push_back(make_shared<LogEvent>(tmp));
+            data->push_back(make_shared<LogEvent>(it));
         }
         VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
         return true;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 745ff74..5a0172b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -195,28 +195,25 @@
           new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
         // Disk Stats
         {android::util::DISK_STATS,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
         // Directory usage
         {android::util::DIRECTORY_USAGE,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
         // Size of app's code, data, and cache
         {android::util::APP_SIZE,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
          {{},
           {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+        // Number of fingerprints registered to each user.
+        {android::util::NUM_FINGERPRINTS,
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
         };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index cf04ee3..f9f1b38 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,44 @@
     }
 }
 
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+    mTagId = statsLogEventWrapper.getTagId();
+    mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
+    mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
+    mLogUid = 0;
+    for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
+        Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+        switch (statsLogEventWrapper.getElements()[i].type) {
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
+                mValues.push_back(
+                        FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
+                mValues.push_back(
+                        FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
+                mValues.push_back(FieldValue(
+                        field, Value(statsLogEventWrapper.getElements()[i].float_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
+                mValues.push_back(FieldValue(
+                        field, Value(statsLogEventWrapper.getElements()[i].double_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
+                mValues.push_back(
+                        FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
+                mValues.push_back(FieldValue(
+                        field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
+                break;
+            default:
+                break;
+        }
+    }
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
     mLogdTimestampNs = wallClockTimestampNs;
     mTagId = tagId;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 2ee6bdf..9ef0bf4 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
 
 #include "FieldValue.h"
 
+#include <android/os/StatsLogEventWrapper.h>
 #include <android/util/ProtoOutputStream.h>
 #include <log/log_event_list.h>
 #include <log/log_read.h>
@@ -61,6 +62,8 @@
      */
     explicit LogEvent(log_msg& msg);
 
+    explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+
     /**
      * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
      */
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a0ab3e4..805e583 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -322,6 +322,11 @@
                 case STRING:
                     protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
                     break;
+                case STORAGE:
+                    protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+                                       (const char*)dim.mValue.storage_value.data(),
+                                       dim.mValue.storage_value.size());
+                    break;
                 default:
                     break;
             }
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 950a258..455e4bb 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -84,7 +84,8 @@
                     cursor.close();
                 }
                 if (provider != null) {
-                    activityManager.removeContentProviderExternal(providerName, token);
+                    activityManager.removeContentProviderExternalAsUser(providerName, token,
+                            UserHandle.USER_SYSTEM);
                 }
             }
         } catch (RemoteException e) {
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index b683138..be9ccec 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -11067,6 +11067,7 @@
 HPLjava/lang/Long;->valueOf(Ljava/lang/String;I)Ljava/lang/Long;
 HPLjava/lang/Math;->addExact(JJ)J
 HPLjava/lang/Math;->scalb(FI)F
+HPLjava/lang/Object;->wait()V
 HPLjava/lang/StackTraceElement;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
 HPLjava/lang/String;->copyValueOf([C)Ljava/lang/String;
 HPLjava/lang/String;->join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8c347fc..0472461 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2134,6 +2134,7 @@
 Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
 Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
 Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
+Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
 Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
 Lcom/android/internal/view/BaseIWindow;-><init>()V
@@ -2226,6 +2227,7 @@
 Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey;
 Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;
 Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey()Ljava/security/PublicKey;
+Lcom/android/org/conscrypt/OpenSSLProvider;-><init>()V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2247,6 +2249,108 @@
 Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List;
 Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
 Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
+Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/MmsException;-><init>()V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
+Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
+Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/PduBody;-><init>()V
+Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
+Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
+Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPart;-><init>()V
+Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
+Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
+Lcom/google/android/mms/pdu/PduPart;->getName()[B
+Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
+Lcom/google/android/mms/pdu/PduPart;->setData([B)V
+Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
+Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
+Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
+Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
+Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
+Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
 Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
 Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
 Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
@@ -2553,6 +2657,7 @@
 Ljava/net/SocketException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
 Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
 Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
+Ljava/net/SocksSocketImpl;-><init>()V
 Ljava/net/URI;->fragment:Ljava/lang/String;
 Ljava/net/URI;->host:Ljava/lang/String;
 Ljava/net/URI;->port:I
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 50e97c5..63c583f 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1436,6 +1436,12 @@
 android.hardware.usb.UsbDeviceConnection
 android.hardware.usb.UsbManager
 android.hardware.usb.UsbRequest
+android.hidl.base.V1_0.DebugInfo
+android.hidl.base.V1_0.IBase
+android.hidl.manager.V1_0.IServiceManager
+android.hidl.manager.V1_0.IServiceManager$Proxy
+android.hidl.manager.V1_0.IServiceNotification
+android.hidl.manager.V1_0.IServiceNotification$Stub
 android.icu.impl.BMPSet
 android.icu.impl.CacheBase
 android.icu.impl.CacheValue
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5499d59..2ee266d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -545,12 +545,12 @@
  * <a name="SavingPersistentState"></a>
  * <h3>Saving Persistent State</h3>
  *
- * <p>There are generally two kinds of persistent state than an activity
+ * <p>There are generally two kinds of persistent state that an activity
  * will deal with: shared document-like data (typically stored in a SQLite
  * database using a {@linkplain android.content.ContentProvider content provider})
  * and internal state such as user preferences.</p>
  *
- * <p>For content provider data, we suggest that activities use a
+ * <p>For content provider data, we suggest that activities use an
  * "edit in place" user model.  That is, any edits a user makes are effectively
  * made immediately without requiring an additional confirmation step.
  * Supporting this model is generally a simple matter of following two rules:</p>
@@ -1383,6 +1383,7 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
         getApplication().dispatchActivityResumed(this);
         mActivityTransitionState.onResume(this, isTopOfTask());
+        enableAutofillCompatibilityIfNeeded();
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 View focus = getCurrentFocus();
@@ -7165,7 +7166,6 @@
         mWindow.setColorMode(info.colorMode);
 
         setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
-        enableAutofillCompatibilityIfNeeded();
     }
 
     private void enableAutofillCompatibilityIfNeeded() {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 16360b3..2490cae 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -263,7 +263,9 @@
     void killAllBackgroundProcesses();
     ContentProviderHolder getContentProviderExternal(in String name, int userId,
             in IBinder token, String tag);
+    /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
     void removeContentProviderExternal(in String name, in IBinder token);
+    void removeContentProviderExternalAsUser(in String name, in IBinder token, int userId);
     // Get memory information about the calling process.
     void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
     boolean killProcessesBelowForeground(in String reason);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 217225e..3638bc4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8253,7 +8253,10 @@
          * <p>For custom display notifications created using {@link #setDisplayIntent},
          * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
          * on their content.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public static final int SIZE_DEFAULT = 0;
 
         /**
@@ -8261,7 +8264,10 @@
          * with an extra small size.
          * <p>This value is only applicable for custom display notifications created using
          * {@link #setDisplayIntent}.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public static final int SIZE_XSMALL = 1;
 
         /**
@@ -8269,7 +8275,10 @@
          * with a small size.
          * <p>This value is only applicable for custom display notifications created using
          * {@link #setDisplayIntent}.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public static final int SIZE_SMALL = 2;
 
         /**
@@ -8277,7 +8286,10 @@
          * with a medium size.
          * <p>This value is only applicable for custom display notifications created using
          * {@link #setDisplayIntent}.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public static final int SIZE_MEDIUM = 3;
 
         /**
@@ -8285,7 +8297,10 @@
          * with a large size.
          * <p>This value is only applicable for custom display notifications created using
          * {@link #setDisplayIntent}.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public static final int SIZE_LARGE = 4;
 
         /**
@@ -8293,20 +8308,29 @@
          * full screen.
          * <p>This value is only applicable for custom display notifications created using
          * {@link #setDisplayIntent}.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public static final int SIZE_FULL_SCREEN = 5;
 
         /**
          * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
          * short amount of time when this notification is displayed on the screen. This
          * is the default value.
+         *
+         * @deprecated This feature is no longer supported.
          */
+        @Deprecated
         public static final int SCREEN_TIMEOUT_SHORT = 0;
 
         /**
          * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
          * for a longer amount of time when this notification is displayed on the screen.
+         *
+         * @deprecated This feature is no longer supported.
          */
+        @Deprecated
         public static final int SCREEN_TIMEOUT_LONG = -1;
 
         /** Notification extra which contains wearable extensions */
@@ -8556,7 +8580,9 @@
          * @param intent the {@link PendingIntent} for an activity
          * @return this object for method chaining
          * @see android.app.Notification.WearableExtender#getDisplayIntent
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public WearableExtender setDisplayIntent(PendingIntent intent) {
             mDisplayIntent = intent;
             return this;
@@ -8565,7 +8591,10 @@
         /**
          * Get the intent to launch inside of an activity view when displaying this
          * notification. This {@code PendingIntent} should be for an activity.
+         *
+         * @deprecated Display intents are no longer supported.
          */
+        @Deprecated
         public PendingIntent getDisplayIntent() {
             return mDisplayIntent;
         }
@@ -8579,7 +8608,9 @@
          * @param page the notification to add as another page
          * @return this object for method chaining
          * @see android.app.Notification.WearableExtender#getPages
+         * @deprecated Multiple content pages are no longer supported.
          */
+        @Deprecated
         public WearableExtender addPage(Notification page) {
             mPages.add(page);
             return this;
@@ -8594,7 +8625,9 @@
          * @param pages a list of notifications
          * @return this object for method chaining
          * @see android.app.Notification.WearableExtender#getPages
+         * @deprecated Multiple content pages are no longer supported.
          */
+        @Deprecated
         public WearableExtender addPages(List<Notification> pages) {
             mPages.addAll(pages);
             return this;
@@ -8604,7 +8637,9 @@
          * Clear all additional pages present on this builder.
          * @return this object for method chaining.
          * @see #addPage
+         * @deprecated Multiple content pages are no longer supported.
          */
+        @Deprecated
         public WearableExtender clearPages() {
             mPages.clear();
             return this;
@@ -8616,7 +8651,9 @@
          * subsequent pages. This field can be used to separate a notification into multiple
          * sections.
          * @return the pages for this notification
+         * @deprecated Multiple content pages are no longer supported.
          */
+        @Deprecated
         public List<Notification> getPages() {
             return mPages;
         }
@@ -8629,7 +8666,9 @@
          * @param background the background bitmap
          * @return this object for method chaining
          * @see android.app.Notification.WearableExtender#getBackground
+         * @deprecated Background images are no longer supported.
          */
+        @Deprecated
         public WearableExtender setBackground(Bitmap background) {
             mBackground = background;
             return this;
@@ -8642,7 +8681,9 @@
          *
          * @return the background image
          * @see android.app.Notification.WearableExtender#setBackground
+         * @deprecated Background images are no longer supported.
          */
+        @Deprecated
         public Bitmap getBackground() {
             return mBackground;
         }
@@ -8688,15 +8729,11 @@
         }
 
         /**
-         * Set an action from this notification's actions to be clickable with the content of
-         * this notification. This action will no longer display separately from the
-         * notification's content.
+         * Set an action from this notification's actions as the primary action. If the action has a
+         * {@link RemoteInput} associated with it, shortcuts to the options for that input are shown
+         * directly on the notification.
          *
-         * <p>For notifications with multiple pages, child pages can also have content actions
-         * set, although the list of available actions comes from the main notification and not
-         * from the child page's notification.
-         *
-         * @param actionIndex The index of the action to hoist onto the current notification page.
+         * @param actionIndex The index of the primary action.
          *                    If wearable actions were added to the main notification, this index
          *                    will apply to that list, otherwise it will apply to the regular
          *                    actions list.
@@ -8707,13 +8744,8 @@
         }
 
         /**
-         * Get the index of the notification action, if any, that was specified as being clickable
-         * with the content of this notification. This action will no longer display separately
-         * from the notification's content.
-         *
-         * <p>For notifications with multiple pages, child pages can also have content actions
-         * set, although the list of available actions comes from the main notification and not
-         * from the child page's notification.
+         * Get the index of the notification action, if any, that was specified as the primary
+         * action.
          *
          * <p>If wearable specific actions were added to the main notification, this index will
          * apply to that list, otherwise it will apply to the regular actions list.
@@ -8938,7 +8970,9 @@
          * qr codes, as well as other simple black-and-white tickets.
          * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
          * @return this object for method chaining
+         * @deprecated This feature is no longer supported.
          */
+        @Deprecated
         public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
             setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
             return this;
@@ -8950,7 +8984,9 @@
          * qr codes, as well as other simple black-and-white tickets.
          * @return {@code true} if it should be displayed in ambient, false otherwise
          * otherwise. The default value is {@code false} if this was never set.
+         * @deprecated This feature is no longer supported.
          */
+        @Deprecated
         public boolean getHintAmbientBigPicture() {
             return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
         }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4f172a4..e532ece 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -25,7 +25,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -214,7 +213,7 @@
     }
 
     /**
-     * Sets the night mode.
+     * Sets the system-wide night mode.
      * <p>
      * The mode can be one of:
      * <ul>
@@ -231,6 +230,12 @@
      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
      * device. Starting in API 23, changes to night mode are always effective.
+     * <p>
+     * Changes to night mode take effect globally and will result in a configuration change
+     * (and potentially an Activity lifecycle event) being applied to all running apps.
+     * Developers interested in an app-local implementation of night mode should consider using
+     * {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the
+     * -night qualifier locally.
      *
      * @param mode the night mode to set
      * @see #getNightMode()
diff --git a/core/java/android/app/backup/package.html b/core/java/android/app/backup/package.html
index 8b5e3ba..dd6c254 100644
--- a/core/java/android/app/backup/package.html
+++ b/core/java/android/app/backup/package.html
@@ -11,7 +11,7 @@
 
 <p>All backup and restore operations are controlled by the {@link
 android.app.backup.BackupManager}. Each application that would
-like to enable backup and preserve its data on remote strage must implement a
+like to enable backup and preserve its data on remote storage must implement a
 backup agent. A backup agent can be built by extending either {@link android.app.backup.BackupAgent}
 or {@link android.app.backup.BackupAgentHelper}. The {@link
 android.app.backup.BackupAgentHelper} class provides a wrapper around {@link
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4c655b5..654bfaf 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -81,7 +81,8 @@
  * {@link #getBondedDevices()}; start device discovery with
  * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
  * listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
  * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
  * </p>
  * <p>This class is thread safe.</p>
@@ -2967,7 +2968,7 @@
     /**
      * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
      * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
-     * for incoming connections.
+     * for incoming connections. The supported Bluetooth transport is LE only.
      * <p>A remote device connecting to this socket will be authenticated and communication on this
      * socket will be encrypted.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
@@ -2977,21 +2978,16 @@
      * closed, Bluetooth is turned off, or the application exits unexpectedly.
      * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
      * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+     * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
      * socket from another Android device that is given the PSM value.
      *
-     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
      * @return an L2CAP CoC BluetoothServerSocket
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions, or unable to start this CoC
-     * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothServerSocket listenUsingL2capCoc(int transport)
+    public BluetoothServerSocket listenUsingL2capChannel()
             throws IOException {
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
         BluetoothServerSocket socket =
                             new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
                                       SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3005,7 +3001,7 @@
             throw new IOException("Error: Unable to assign PSM value");
         }
         if (DBG) {
-            Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+            Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
                     + assignedPsm);
         }
         socket.setChannel(assignedPsm);
@@ -3014,10 +3010,23 @@
     }
 
     /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, listenUsingL2capChannel.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothServerSocket listenUsingL2capCoc(int transport)
+            throws IOException {
+        Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel");
+        return listenUsingL2capChannel();
+    }
+
+    /**
      * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
-     * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+     * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
+     * supported Bluetooth transport is LE only.
      * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
-     * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+     * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
      * authenticated communication channel is desired.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
      * {@link BluetoothServerSocket}.
@@ -3027,21 +3036,16 @@
      * unexpectedly.
      * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
      * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
-     * server socket from another Android device that is given the PSM value.
+     * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
+     * socket from another Android device that is given the PSM value.
      *
-     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
      * @return an L2CAP CoC BluetoothServerSocket
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions, or unable to start this CoC
-     * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+    public BluetoothServerSocket listenUsingInsecureL2capChannel()
             throws IOException {
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
         BluetoothServerSocket socket =
                             new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
                                       SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3055,11 +3059,24 @@
             throw new IOException("Error: Unable to assign PSM value");
         }
         if (DBG) {
-            Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+            Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
                     + assignedPsm);
         }
         socket.setChannel(assignedPsm);
 
         return socket;
     }
+
+    /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, listenUsingInsecureL2capChannel.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+            throws IOException {
+        Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, "
+                    + "listenUsingInsecureL2capChannel");
+        return listenUsingInsecureL2capChannel();
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 818a749..73e98cd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1963,8 +1963,8 @@
     /**
      * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
      * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value.
-     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
      * peer-peer Bluetooth applications.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
      * <p>Application using this API is responsible for obtaining PSM value from remote device.
@@ -1975,59 +1975,71 @@
      * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
      * int)}.
      *
-     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
      * @param psm dynamic PSM value from remote device
      * @return a CoC #BluetoothSocket ready for an outgoing connection
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions
-     * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+    public BluetoothSocket createL2capChannel(int psm) throws IOException {
         if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+            Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
             throw new IOException();
         }
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
-        if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+        if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
         return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
                 null);
     }
 
     /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, createL2capChannel.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+        Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel");
+        return createL2capChannel(psm);
+    }
+
+    /**
      * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
      * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value.
-     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
-     * for peer-peer Bluetooth applications.
+     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+     * <p>This is designed to be used with {@link
+     * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
      * <p>Application using this API is responsible for obtaining PSM value from remote device.
      * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
-     * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+     * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
      * authenticated communication channel is possible.
      *
-     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
      * @param psm dynamic PSM value from remote device
      * @return a CoC #BluetoothSocket ready for an outgoing connection
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
+        if (!isBluetoothEnabled()) {
+            Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
+            throw new IOException();
+        }
+        if (DBG) {
+            Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
+        }
+        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+                null);
+    }
+
+    /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, createInsecureL2capChannel.
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
-            throw new IOException();
-        }
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
-        if (DBG) {
-            Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
-                null);
+        Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
+        return createInsecureL2capChannel(psm);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ba4b5a5..5fc344a 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,12 +203,11 @@
     /**
      * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
      * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
-     * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
-     * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+     * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+     * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
      * method is called on non-L2CAP server sockets.
      *
      * @return the assigned PSM or LE_PSM value depending on transport
-     * @hide
      */
     public int getPsm() {
         return mChannel;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 780f896..3a1e2f5 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -667,6 +667,10 @@
      * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
      */
     public int getConnectionType() {
+        if (mType == TYPE_L2CAP_LE) {
+            // Treat the LE CoC to be the same type as L2CAP.
+            return TYPE_L2CAP;
+        }
         return mType;
     }
 
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 3fe17840..8760efe 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -95,9 +95,6 @@
      * Sets the current primary clip on the clipboard.  This is the clip that
      * is involved in normal cut and paste operations.
      *
-     * <em>If the application is not the default IME or does not have input focus this will have
-     * no effect.</em>
-     *
      * @param clip The clipped data item to set.
      * @see #getPrimaryClip()
      * @see #clearPrimaryClip()
@@ -115,9 +112,6 @@
     /**
      * Clears any current primary clip on the clipboard.
      *
-     * <em>If the application is not the default IME or does not have input focus this will have
-     * no effect.</em>
-     *
      * @see #setPrimaryClip(ClipData)
      */
     public void clearPrimaryClip() {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2ae3ae6..d88f6e3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3167,11 +3167,11 @@
      *
      * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
      * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
-     * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
-     * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
-     * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
-     * Generally, if you are running as an instant app you should always check whether the result
-     * of this method is null.
+     * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+     * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+     * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+     * return <code>null</code>.  Generally, if you are running as an instant app you should always
+     * check whether the result of this method is null.
      *
      * @param name The name of the desired service.
      *
@@ -3258,11 +3258,11 @@
      *
      * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
      * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
-     * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
-     * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
-     * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
-     * Generally, if you are running as an instant app you should always check whether the result
-     * of this method is null.
+     * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+     * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+     * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+     * return <code>null</code>.  Generally, if you are running as an instant app you should always
+     * check whether the result of this method is null.
      *
      * @param serviceClass The class of the desired service.
      * @return The service or null if the class is not a supported system service.
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 0dd6186..0469a90 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -125,7 +125,7 @@
  *
  * <p><strong>Data Authority</strong> matches if any of the given values match
  * the Intent's data authority <em>and</em> one of the data schemes in the filter
- * has matched the Intent, <em>or</em> no authories were supplied in the filter.
+ * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
  * The Intent authority is determined by calling
  * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
  * <em>Note that authority matching here is <b>case sensitive</b>, unlike
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 0c70a3d..19af609 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -22,7 +22,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -103,8 +102,14 @@
 
     private static volatile boolean sForceSafeLabels = false;
 
-    /** {@hide} */
-    @UnsupportedAppUsage
+    /**
+     * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}.
+     *
+     * @param forceSafeLabels {@code true} to enforce safe labels
+     *
+     * @hide
+     */
+    @SystemApi
     public static void setForceSafeLabels(boolean forceSafeLabels) {
         sForceSafeLabels = forceSafeLabels;
     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1b4878c..a15711f5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1979,6 +1979,8 @@
      * </ul>
      * A version of 1.1.0 or higher also indicates:
      * <ul>
+     * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is
+     *     supported.</li>
      * <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
      * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
      *     supported.</li>
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index dbb2527..c604ff1 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -202,31 +202,6 @@
     }
 
     /**
-     * @param error
-     * @param vendorCode
-     * @return the error string associated with this error
-     */
-    default String getErrorString(int error, int vendorCode) {
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    /**
-     * @param acquireInfo
-     * @param vendorCode
-     * @return the help string associated with this code
-     */
-    default String getAcquiredString(int acquireInfo, int vendorCode) {
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    /**
-     * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
-     */
-    default int getType() {
-        throw new UnsupportedOperationException("Stub!");
-    }
-
-    /**
      * This call warms up the hardware and starts scanning for valid biometrics. It terminates
      * when {@link AuthenticationCallback#onAuthenticationError(int,
      * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 0f83c8b..20e0116 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -168,10 +168,11 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception while authenticating: ", e);
                 if (callback != null) {
-                    // Though this may not be a hardware issue, it will cause apps to give up or try
-                    // again later.
+                    // Though this may not be a hardware issue, it will cause apps to give up or
+                    // try again later.
                     callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
-                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                            getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                                0 /* vendorCode */));
                 }
             }
         }
@@ -232,10 +233,11 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception in enroll: ", e);
                 if (callback != null) {
-                    // Though this may not be a hardware issue, it will cause apps to give up or try
-                    // again later.
+                    // Though this may not be a hardware issue, it will cause apps to give up or
+                    // try again later.
                     callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
-                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                            getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                                0 /* vendorCode */));
                 }
             }
         }
@@ -315,7 +317,8 @@
                 Log.w(TAG, "Remote exception in remove: ", e);
                 if (callback != null) {
                     callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
-                            getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                            getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                                0 /* vendorCode */));
                 }
             }
         }
@@ -513,32 +516,34 @@
         }
     }
 
-    @Override
-    public String getErrorString(int errMsg, int vendorCode) {
+    /**
+     * @hide
+     */
+    public static String getErrorString(Context context, int errMsg, int vendorCode) {
         switch (errMsg) {
             case FACE_ERROR_HW_UNAVAILABLE:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.face_error_hw_not_available);
             case FACE_ERROR_UNABLE_TO_PROCESS:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.face_error_unable_to_process);
             case FACE_ERROR_TIMEOUT:
-                return mContext.getString(com.android.internal.R.string.face_error_timeout);
+                return context.getString(com.android.internal.R.string.face_error_timeout);
             case FACE_ERROR_NO_SPACE:
-                return mContext.getString(com.android.internal.R.string.face_error_no_space);
+                return context.getString(com.android.internal.R.string.face_error_no_space);
             case FACE_ERROR_CANCELED:
-                return mContext.getString(com.android.internal.R.string.face_error_canceled);
+                return context.getString(com.android.internal.R.string.face_error_canceled);
             case FACE_ERROR_LOCKOUT:
-                return mContext.getString(com.android.internal.R.string.face_error_lockout);
+                return context.getString(com.android.internal.R.string.face_error_lockout);
             case FACE_ERROR_LOCKOUT_PERMANENT:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.face_error_lockout_permanent);
             case FACE_ERROR_NOT_ENROLLED:
-                return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+                return context.getString(com.android.internal.R.string.face_error_not_enrolled);
             case FACE_ERROR_HW_NOT_PRESENT:
-                return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+                return context.getString(com.android.internal.R.string.face_error_hw_not_present);
             case FACE_ERROR_VENDOR: {
-                String[] msgArray = mContext.getResources().getStringArray(
+                String[] msgArray = context.getResources().getStringArray(
                         com.android.internal.R.array.face_error_vendor);
                 if (vendorCode < msgArray.length) {
                     return msgArray[vendorCode];
@@ -552,35 +557,34 @@
     /**
      * @hide
      */
-    @Override
-    public String getAcquiredString(int acquireInfo, int vendorCode) {
+    public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
         switch (acquireInfo) {
             case FACE_ACQUIRED_GOOD:
                 return null;
             case FACE_ACQUIRED_INSUFFICIENT:
-                return mContext.getString(R.string.face_acquired_insufficient);
+                return context.getString(R.string.face_acquired_insufficient);
             case FACE_ACQUIRED_TOO_BRIGHT:
-                return mContext.getString(R.string.face_acquired_too_bright);
+                return context.getString(R.string.face_acquired_too_bright);
             case FACE_ACQUIRED_TOO_DARK:
-                return mContext.getString(R.string.face_acquired_too_dark);
+                return context.getString(R.string.face_acquired_too_dark);
             case FACE_ACQUIRED_TOO_CLOSE:
-                return mContext.getString(R.string.face_acquired_too_close);
+                return context.getString(R.string.face_acquired_too_close);
             case FACE_ACQUIRED_TOO_FAR:
-                return mContext.getString(R.string.face_acquired_too_far);
+                return context.getString(R.string.face_acquired_too_far);
             case FACE_ACQUIRED_TOO_HIGH:
-                return mContext.getString(R.string.face_acquired_too_high);
+                return context.getString(R.string.face_acquired_too_high);
             case FACE_ACQUIRED_TOO_LOW:
-                return mContext.getString(R.string.face_acquired_too_low);
+                return context.getString(R.string.face_acquired_too_low);
             case FACE_ACQUIRED_TOO_RIGHT:
-                return mContext.getString(R.string.face_acquired_too_right);
+                return context.getString(R.string.face_acquired_too_right);
             case FACE_ACQUIRED_TOO_LEFT:
-                return mContext.getString(R.string.face_acquired_too_left);
+                return context.getString(R.string.face_acquired_too_left);
             case FACE_ACQUIRED_POOR_GAZE:
-                return mContext.getString(R.string.face_acquired_poor_gaze);
+                return context.getString(R.string.face_acquired_poor_gaze);
             case FACE_ACQUIRED_NOT_DETECTED:
-                return mContext.getString(R.string.face_acquired_not_detected);
+                return context.getString(R.string.face_acquired_not_detected);
             case FACE_ACQUIRED_VENDOR: {
-                String[] msgArray = mContext.getResources().getStringArray(
+                String[] msgArray = context.getResources().getStringArray(
                         R.array.face_acquired_vendor);
                 if (vendorCode < msgArray.length) {
                     return msgArray[vendorCode];
@@ -592,18 +596,10 @@
     }
 
     /**
-     * @hide
-     */
-    @Override
-    public int getType() {
-        return TYPE_FACE;
-    }
-
-    /**
      * Used so BiometricPrompt can map the face ones onto existing public constants.
      * @hide
      */
-    public int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
+    public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
         switch (acquireInfo) {
             case FACE_ACQUIRED_GOOD:
                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
@@ -898,13 +894,13 @@
                 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
         if (mEnrollmentCallback != null) {
             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         } else if (mAuthenticationCallback != null) {
             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         } else if (mRemovalCallback != null) {
             mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         }
     }
 
@@ -932,7 +928,7 @@
         if (mAuthenticationCallback != null) {
             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
         }
-        final String msg = getAcquiredString(acquireInfo, vendorCode);
+        final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
         if (msg == null) {
             return;
         }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b380a2e..a4f3ce1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -426,7 +426,8 @@
                 // Though this may not be a hardware issue, it will cause apps to give up or try
                 // again later.
                 callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                        getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */));
             }
         }
     }
@@ -476,7 +477,8 @@
                 // Though this may not be a hardware issue, it will cause apps to give up or try
                 // again later.
                 callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                        getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */));
             }
         }
     }
@@ -546,7 +548,8 @@
             Slog.w(TAG, "Remote exception in remove: ", e);
             if (callback != null) {
                 callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                        getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */));
             }
         }
     }
@@ -568,7 +571,8 @@
             Slog.w(TAG, "Remote exception in enumerate: ", e);
             if (callback != null) {
                 callback.onEnumerateError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                        getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+                        getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */));
             }
         }
     }
@@ -862,7 +866,7 @@
         if (mAuthenticationCallback != null) {
             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
         }
-        final String msg = getAcquiredString(acquireInfo, vendorCode);
+        final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
         if (msg == null) {
             return;
         }
@@ -882,16 +886,16 @@
                 ? (vendorCode + FINGERPRINT_ERROR_VENDOR_BASE) : errMsgId;
         if (mEnrollmentCallback != null) {
             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         } else if (mAuthenticationCallback != null) {
             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         } else if (mRemovalCallback != null) {
             mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         } else if (mEnumerateCallback != null) {
             mEnumerateCallback.onEnumerateError(clientErrMsgId,
-                    getErrorString(errMsgId, vendorCode));
+                    getErrorString(mContext, errMsgId, vendorCode));
         }
     }
 
@@ -934,38 +938,37 @@
     /**
      * @hide
      */
-    @Override
-    public String getErrorString(int errMsg, int vendorCode) {
+    public static String getErrorString(Context context, int errMsg, int vendorCode) {
         switch (errMsg) {
             case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_error_unable_to_process);
             case FINGERPRINT_ERROR_HW_UNAVAILABLE:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_error_hw_not_available);
             case FINGERPRINT_ERROR_NO_SPACE:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_error_no_space);
             case FINGERPRINT_ERROR_TIMEOUT:
-                return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout);
+                return context.getString(com.android.internal.R.string.fingerprint_error_timeout);
             case FINGERPRINT_ERROR_CANCELED:
-                return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
+                return context.getString(com.android.internal.R.string.fingerprint_error_canceled);
             case FINGERPRINT_ERROR_LOCKOUT:
-                return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
+                return context.getString(com.android.internal.R.string.fingerprint_error_lockout);
             case FINGERPRINT_ERROR_LOCKOUT_PERMANENT:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.fingerprint_error_lockout_permanent);
             case FINGERPRINT_ERROR_USER_CANCELED:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.fingerprint_error_user_canceled);
             case FINGERPRINT_ERROR_NO_FINGERPRINTS:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.fingerprint_error_no_fingerprints);
             case FINGERPRINT_ERROR_HW_NOT_PRESENT:
-                return mContext.getString(
+                return context.getString(
                         com.android.internal.R.string.fingerprint_error_hw_not_present);
             case FINGERPRINT_ERROR_VENDOR: {
-                    String[] msgArray = mContext.getResources().getStringArray(
+                    String[] msgArray = context.getResources().getStringArray(
                             com.android.internal.R.array.fingerprint_error_vendor);
                     if (vendorCode < msgArray.length) {
                         return msgArray[vendorCode];
@@ -979,28 +982,27 @@
     /**
      * @hide
      */
-    @Override
-    public String getAcquiredString(int acquireInfo, int vendorCode) {
+    public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
         switch (acquireInfo) {
             case FINGERPRINT_ACQUIRED_GOOD:
                 return null;
             case FINGERPRINT_ACQUIRED_PARTIAL:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_acquired_partial);
             case FINGERPRINT_ACQUIRED_INSUFFICIENT:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_acquired_insufficient);
             case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_acquired_imager_dirty);
             case FINGERPRINT_ACQUIRED_TOO_SLOW:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_acquired_too_slow);
             case FINGERPRINT_ACQUIRED_TOO_FAST:
-                return mContext.getString(
+                return context.getString(
                     com.android.internal.R.string.fingerprint_acquired_too_fast);
             case FINGERPRINT_ACQUIRED_VENDOR: {
-                    String[] msgArray = mContext.getResources().getStringArray(
+                    String[] msgArray = context.getResources().getStringArray(
                             com.android.internal.R.array.fingerprint_acquired_vendor);
                     if (vendorCode < msgArray.length) {
                         return msgArray[vendorCode];
@@ -1011,14 +1013,6 @@
         return null;
     }
 
-    /**
-     * @hide
-     */
-    @Override
-    public int getType() {
-        return TYPE_FINGERPRINT;
-    }
-
     private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
 
         @Override // binder call
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 34fa5b6..b948402 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -85,6 +85,7 @@
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -465,7 +466,7 @@
         public final void initializeInternal(IBinder token, int displayId,
                 IInputMethodPrivilegedOperations privilegedOperations) {
             mPrivOps.set(privilegedOperations);
-            mImm.registerInputMethodPrivOps(token, mPrivOps);
+            InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
             updateInputMethodDisplay(displayId);
             attachToken(token);
         }
@@ -1031,7 +1032,7 @@
         if (mToken != null) {
             // This is completely optional, but allows us to show more explicit error messages
             // when IME developers are doing something unsupported.
-            mImm.unregisterInputMethodPrivOps(mToken);
+            InputMethodPrivilegedOperationsRegistry.remove(mToken);
         }
     }
 
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 02819a6..080e058 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -38,7 +38,7 @@
  * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
  * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
  * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
- * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
+ * <li>MIFARE Classic 4k are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
  * and the last 8 sectors contain 16 blocks.
  * </ul>
  *
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index b13bcac..051ab75 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -15,8 +15,10 @@
  */
 package android.os;
 
-import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Wrapper class for sending data from Android OS to StatsD.
@@ -24,39 +26,28 @@
  * @hide
  */
 public final class StatsLogEventWrapper implements Parcelable {
-    private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+    static final boolean DEBUG = false;
+    static final String TAG = "StatsLogEventWrapper";
 
-    // Below are constants copied from log/log.h
-    private static final int EVENT_TYPE_INT = 0;  /* int32_t */
-    private static final int EVENT_TYPE_LONG = 1; /* int64_t */
-    private static final int EVENT_TYPE_STRING = 2;
-    private static final int EVENT_TYPE_LIST = 3;
-    private static final int EVENT_TYPE_FLOAT = 4;
+    // Keep in sync with FieldValue.h enums
+    private static final int EVENT_TYPE_UNKNOWN = 0;
+    private static final int EVENT_TYPE_INT = 1; /* int32_t */
+    private static final int EVENT_TYPE_LONG = 2; /* int64_t */
+    private static final int EVENT_TYPE_FLOAT = 3;
+    private static final int EVENT_TYPE_DOUBLE = 4;
+    private static final int EVENT_TYPE_STRING = 5;
+    private static final int EVENT_TYPE_STORAGE = 6;
 
-    // Keep this in sync with system/core/logcat/event.logtags
-    private static final int STATS_BUFFER_TAG_ID = 1937006964;
-    /**
-     * Creates a log_event that is binary-encoded as implemented in
-     * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
-     * for pushed and pulled data. The write* methods must be called in the same order as their
-     * field number. There is no checking that the correct number of write* methods is called.
-     * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
-     * may be unnecessary.
-     *
-     * @param tag    The integer representing the tag for this event.
-     * @param fields The number of fields specified in this event.
-     */
-    public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
-        // Write four bytes from tag, starting with least-significant bit.
-        // For pulled data, this tag number is not really used. We use the same tag number as
-        // pushed ones to be consistent.
-        write4Bytes(STATS_BUFFER_TAG_ID);
-        mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
-        mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
-        // The first element is the elapsed realtime.
-        writeLong(elapsedNanos);
-        // The second element is the real atom tag number
-        writeInt(tag);
+    List<Integer> mTypes = new ArrayList<>();
+    List<Object> mValues = new ArrayList<>();
+    int mTag;
+    long mElapsedTimeNs;
+    long mWallClockTimeNs;
+
+    public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
+        this.mTag = tag;
+        this.mElapsedTimeNs = elapsedTimeNs;
+        this.mWallClockTimeNs = wallClockTimeNs;
     }
 
     /**
@@ -79,69 +70,80 @@
                 }
             };
 
-    private void write4Bytes(int val) {
-        mStorage.write(val);
-        mStorage.write(val >>> 8);
-        mStorage.write(val >>> 16);
-        mStorage.write(val >>> 24);
-    }
-
-    private void write8Bytes(long val) {
-        write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
-        write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
-    }
-
-    /**
-     * Adds 32-bit integer to output.
-     */
     public void writeInt(int val) {
-        mStorage.write(EVENT_TYPE_INT);
-        write4Bytes(val);
+        mTypes.add(EVENT_TYPE_INT);
+        mValues.add(val);
     }
 
-    /**
-     * Adds 64-bit long to output.
-     */
     public void writeLong(long val) {
-        mStorage.write(EVENT_TYPE_LONG);
-        write8Bytes(val);
+        mTypes.add(EVENT_TYPE_LONG);
+        mValues.add(val);
     }
 
     /**
-     * Adds a 4-byte floating point value to output.
-     */
-    public void writeFloat(float val) {
-        int v = Float.floatToIntBits(val);
-        mStorage.write(EVENT_TYPE_FLOAT);
-        write4Bytes(v);
-    }
-
-    /**
-     * Adds a string to the output.
+     * Write a string value.
      */
     public void writeString(String val) {
-        mStorage.write(EVENT_TYPE_STRING);
-        write4Bytes(val.length());
-        byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
-        mStorage.write(bytes, 0, bytes.length);
+        mTypes.add(EVENT_TYPE_STRING);
+        // use empty string for null
+        mValues.add(val == null ? "" : val);
+    }
+
+    public void writeFloat(float val) {
+        mTypes.add(EVENT_TYPE_FLOAT);
+        mValues.add(val);
     }
 
     /**
-     * Adds a boolean by adding either a 1 or 0 to the output.
+     * Write a storage value.
      */
+    public void writeStorage(byte[] val) {
+        mTypes.add(EVENT_TYPE_STORAGE);
+        mValues.add(val);
+    }
+
     public void writeBoolean(boolean val) {
-        int toWrite = val ? 1 : 0;
-        mStorage.write(EVENT_TYPE_INT);
-        write4Bytes(toWrite);
+        mTypes.add(EVENT_TYPE_INT);
+        mValues.add(val ? 1 : 0);
     }
 
     /**
      * Writes the stored fields to a byte array. Will first write a new-line character to denote
      * END_LIST before writing contents to byte array.
      */
+
     public void writeToParcel(Parcel out, int flags) {
-        mStorage.write(10); // new-line character is same as END_LIST
-        out.writeByteArray(mStorage.toByteArray());
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
+                            + mTypes.size() + " elements.");
+        }
+        out.writeInt(mTag);
+        out.writeLong(mElapsedTimeNs);
+        out.writeLong(mWallClockTimeNs);
+        out.writeInt(mTypes.size());
+        for (int i = 0; i < mTypes.size(); i++) {
+            out.writeInt(mTypes.get(i));
+            switch (mTypes.get(i)) {
+                case EVENT_TYPE_INT:
+                    out.writeInt((int) mValues.get(i));
+                    break;
+                case EVENT_TYPE_LONG:
+                    out.writeLong((long) mValues.get(i));
+                    break;
+                case EVENT_TYPE_FLOAT:
+                    out.writeFloat((float) mValues.get(i));
+                    break;
+                case EVENT_TYPE_STRING:
+                    out.writeString((String) mValues.get(i));
+                    break;
+                case EVENT_TYPE_STORAGE:
+                    out.writeByteArray((byte[]) mValues.get(i));
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index e0bffae..18aea03 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,8 +16,8 @@
 
 package android.permissionpresenterservice;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
@@ -74,15 +74,13 @@
     public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
 
     /**
-     * Revoke the permission {@code permissionName} for app {@code packageName}
+     * Revokes the permission {@code permissionName} for app {@code packageName}
      *
      * @param packageName The package for which to revoke
      * @param permissionName The permission to revoke
-     *
-     * @hide
      */
-    @UnsupportedAppUsage
-    public abstract void onRevokeRuntimePermission(String packageName, String permissionName);
+    public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permissionName);
 
     @Override
     public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 1695c13..3893f2a 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.RemoteException;
+import android.util.Log;
 
 /**
  * <p><code>FillCallback</code> handles autofill requests from the {@link AutofillService} into
@@ -28,6 +29,9 @@
  * <a href="/guide/topics/text/autofill-services">Build autofill services</a>.
  */
 public final class FillCallback {
+
+    private static final String TAG = "FillCallback";
+
     private final IFillCallback mCallback;
     private final int mRequestId;
     private boolean mCalled;
@@ -39,13 +43,20 @@
     }
 
     /**
-     * Notifies the Android System that an
-     * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
-     * FillCallback)} was successfully fulfilled by the service.
+     * Notifies the Android System that a fill request
+     * ({@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+     * FillCallback)}) was successfully fulfilled by the service.
      *
-     * @param response autofill information for that activity, or {@code null} when the activity
-     * cannot be autofilled (for example, if it only contains read-only fields). See
-     * {@link FillResponse} for examples.
+     * <p>This method should always be called, even if the service doesn't have the heuristics to
+     * fulfill the request (in which case it should be called with {@code null}).
+     *
+     * <p>See the main {@link AutofillService} documentation for more details and examples.
+     *
+     * @param response autofill information for that activity, or {@code null} when the service
+     * cannot autofill the activity.
+     *
+     * @throws IllegalStateException if this method or {@link #onFailure(CharSequence)} was already
+     * called.
      */
     public void onSuccess(@Nullable FillResponse response) {
         assertNotCalled();
@@ -63,13 +74,28 @@
     }
 
     /**
-     * Notifies the Android System that an
+     * Notifies the Android System that a fill request (
      * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
-     * FillCallback)} could not be fulfilled by the service.
+     * FillCallback)}) could not be fulfilled by the service (for example, because the user data was
+     * not available yet), so the request could be retried later.
      *
-     * @param message error message to be displayed to the user.
+     * <p><b>Note: </b>this method should not be used when the service didn't have the heursitics to
+     * fulfill the request; in this case, the service should call {@link #onSuccess(FillResponse)
+     * onSuccess(null)} instead.
+     *
+     * <p><b>Note: </b>on Android versions up to {@link android.os.Build.VERSION_CODES#P}, this
+     * method is not working as intended, and the service should call
+     * {@link #onSuccess(FillResponse) onSuccess(null)} instead.
+     *
+     * @param message error message to be displayed to the user. <b>Note: </b> this message is
+     * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+     * Information, such as username or email address).
+     *
+     * @throws IllegalStateException if this method or {@link #onSuccess(FillResponse)} was already
+     * called.
      */
     public void onFailure(@Nullable CharSequence message) {
+        Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
         assertNotCalled();
         mCalled = true;
         try {
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 855981a..0625095 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.content.IntentSender;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
@@ -29,6 +30,9 @@
  * autofilled.
  */
 public final class SaveCallback {
+
+    private static final String TAG = "SaveCallback";
+
     private final ISaveCallback mCallback;
     private boolean mCalled;
 
@@ -41,6 +45,9 @@
      * Notifies the Android System that an
      * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled
      * by the service.
+     *
+     * @throws IllegalStateException if this method, {@link #onSuccess(IntentSender)}, or
+     * {@link #onFailure(CharSequence)} was already called.
      */
     public void onSuccess() {
         onSuccessInternal(null);
@@ -58,6 +65,9 @@
      *
      * @param intentSender intent that will be launched from the context of activity being
      * autofilled.
+     *
+     * @throws IllegalStateException if this method, {@link #onSuccess()},
+     * or {@link #onFailure(CharSequence)} was already called.
      */
     public void onSuccess(@NonNull IntentSender intentSender) {
         onSuccessInternal(Preconditions.checkNotNull(intentSender));
@@ -86,9 +96,15 @@
      * you prefer to show your own message, call {@link #onSuccess()} or
      * {@link #onSuccess(IntentSender)} instead.
      *
-     * @param message error message to be displayed to the user.
+     * @param message error message to be displayed to the user. <b>Note: </b> this message is
+     * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+     * Information, such as username or email address).
+     *
+     * @throws IllegalStateException if this method, {@link #onSuccess()},
+     * or {@link #onSuccess(IntentSender)} was already called.
      */
     public void onFailure(CharSequence message) {
+        Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
         assertNotCalled();
         mCalled = true;
         try {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index f571956..dfaf49a 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -235,12 +235,17 @@
      */
     public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
 
+
     /**
-     * Don't trigger the autofill save UI when the autofill context associated with the response
-     * associated with this {@link SaveInfo} is {@link AutofillManager#commit() committed},
-     * but keep its {@link FillContext} so it's delivered in a future
-     * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request} of an
-     * activity belonging to the same task.
+     * Postpone the autofill save UI.
+     *
+     * <p>If flag is set, the autofill save UI is not triggered when the
+     * autofill context associated with the response associated with this {@link SaveInfo} is
+     * committed (with {@link AutofillManager#commit()}). Instead, the {@link FillContext}
+     * is delivered in future fill requests (with {@link
+     * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)})
+     * and save request (with {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)})
+     * of an activity belonging to the same task.
      *
      * <p>This flag should be used when the service detects that the application uses
      * multiple screens to implement an autofillable workflow (for example, one screen for the
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 26240c5..c1a3c2b 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -283,6 +283,9 @@
                         } catch (android.os.RemoteException ex) {
                             Log.v(TAG, "Unable to contact notification manager", ex);
                             throw ex.rethrowFromSystemServer();
+                        } catch (SecurityException e) {
+                            // app cannot catch and recover from this, so do on their behalf
+                            Log.w(TAG, "Enqueue adjustment failed; no longer connected", e);
                         }
                     }
                     break;
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e3d68a6..24819a6 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -16,6 +16,8 @@
 
 package android.service.voice;
 
+import com.android.internal.app.IVoiceActionCheckCallback;
+
 /**
  * @hide
  */
@@ -24,4 +26,6 @@
     void soundModelsChanged();
     void shutdown();
     void launchVoiceAssistFromKeyguard();
+    void getActiveServiceSupportedActions(in List<String> voiceActions,
+     in IVoiceActionCheckCallback callback);
 }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0bbc07e..e105fdf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,6 +16,8 @@
 
 package android.service.voice;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
@@ -26,17 +28,22 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 /**
  * Top-level service of the current global voice interactor, which is providing
@@ -71,23 +78,43 @@
     public static final String SERVICE_META_DATA = "android.voice_interaction";
 
     IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
-        @Override public void ready() {
-            mHandler.sendEmptyMessage(MSG_READY);
-        }
-        @Override public void shutdown() {
-            mHandler.sendEmptyMessage(MSG_SHUTDOWN);
-        }
-        @Override public void soundModelsChanged() {
-            mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
-        }
         @Override
-        public void launchVoiceAssistFromKeyguard() throws RemoteException {
-            mHandler.sendEmptyMessage(MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD);
+        public void ready() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onReady, VoiceInteractionService.this));
+        }
+
+        @Override
+        public void shutdown() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+        }
+
+        @Override
+        public void soundModelsChanged() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onSoundModelsChangedInternal,
+                    VoiceInteractionService.this));
+        }
+
+        @Override
+        public void launchVoiceAssistFromKeyguard() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onLaunchVoiceAssistFromKeyguard,
+                    VoiceInteractionService.this));
+        }
+
+        @Override
+        public void getActiveServiceSupportedActions(List<String> voiceActions,
+                IVoiceActionCheckCallback callback) {
+            Handler.getMain().executeOrSendMessage(
+                    PooledLambda.obtainMessage(VoiceInteractionService::onHandleVoiceActionCheck,
+                            VoiceInteractionService.this,
+                            voiceActions,
+                            callback));
         }
     };
 
-    MyHandler mHandler;
-
     IVoiceInteractionManagerService mSystemService;
 
     private final Object mLock = new Object();
@@ -96,33 +123,6 @@
 
     private AlwaysOnHotwordDetector mHotwordDetector;
 
-    static final int MSG_READY = 1;
-    static final int MSG_SHUTDOWN = 2;
-    static final int MSG_SOUND_MODELS_CHANGED = 3;
-    static final int MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD = 4;
-
-    class MyHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_READY:
-                    onReady();
-                    break;
-                case MSG_SHUTDOWN:
-                    onShutdownInternal();
-                    break;
-                case MSG_SOUND_MODELS_CHANGED:
-                    onSoundModelsChangedInternal();
-                    break;
-                case MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD:
-                    onLaunchVoiceAssistFromKeyguard();
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    }
-
     /**
      * Called when a user has activated an affordance to launch voice assist from the Keyguard.
      *
@@ -186,7 +186,7 @@
      * be any combination of
      * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
      * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
-      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
+     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
      * to request that the system generate and deliver assist data on the current foreground
      * app as part of showing the session UI.
      */
@@ -200,10 +200,22 @@
         }
     }
 
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mHandler = new MyHandler();
+    /**
+     * Request to query for what extended voice actions this service supports. This method will
+     * be called when the system checks the supported actions of this
+     * {@link VoiceInteractionService}. Supported actions may be delivered to
+     * {@link VoiceInteractionSession} later to request a session to perform an action.
+     *
+     * <p>Voice actions are defined in support libraries and could vary based on platform context.
+     * For example, car related voice actions will be defined in car support libraries.
+     *
+     * @param voiceActions A set of checked voice actions.
+     * @return Returns a subset of checked voice actions. Additional voice actions in the
+     * returned set will be ignored. Returns null or empty set if no actions are supported.
+     */
+    @Nullable
+    public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
+        return null;
     }
 
     @Override
@@ -254,6 +266,18 @@
         }
     }
 
+    private void onHandleVoiceActionCheck(List<String> voiceActions,
+            IVoiceActionCheckCallback callback) {
+        if (callback != null) {
+            try {
+                Set<String> voiceActionsSet = new ArraySet<>(voiceActions);
+                Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet);
+                callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet));
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /**
      * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
      * This instance must be retained and used by the client.
@@ -289,12 +313,12 @@
     }
 
     /**
-      * Checks if a given keyphrase and locale are supported to create an
-      * {@link AlwaysOnHotwordDetector}.
-      *
-      * @return true if the keyphrase and locale combination is supported, false otherwise.
-      * @hide
-      */
+     * Checks if a given keyphrase and locale are supported to create an
+     * {@link AlwaysOnHotwordDetector}.
+     *
+     * @return true if the keyphrase and locale combination is supported, false otherwise.
+     * @hide
+     */
     @UnsupportedAppUsage
     public final boolean isKeyphraseAndLocaleSupportedForHotword(String keyphrase, Locale locale) {
         if (mKeyphraseEnrollmentInfo == null) {
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6edf845..6c15446 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -58,6 +58,6 @@
     /**
      * Just like {@link Paint#getTextRunCursor}.
      */
-    int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
+    int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
             int cursorOpt, Paint p);
 }
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 33c977b..c89617f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -619,7 +619,7 @@
                     previousLineBottom = lbottom;
                     int lbaseline = lbottom - getLineDescent(i);
 
-                    if (start >= spanEnd) {
+                    if (end >= spanEnd) {
                         // These should be infrequent, so we'll use this so that
                         // we don't have to check as often.
                         spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 9d841e8..c5fabaf 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1551,7 +1551,7 @@
      *
      * @param contextStart the start index of the context
      * @param contextEnd the (non-inclusive) end index of the context
-     * @param dir either DIRECTION_RTL or DIRECTION_LTR
+     * @param dir 1 if the run is RTL, otherwise 0
      * @param offset the cursor position to move from
      * @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
      * CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
@@ -1563,21 +1563,28 @@
     @Deprecated
     public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
             int cursorOpt, Paint p) {
+        return getTextRunCursor(contextStart, contextEnd, dir == 1, offset, cursorOpt, p);
+    }
+
+    /** @hide */
+    @Override
+    public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
+            int cursorOpt, Paint p) {
 
         int ret;
 
         int contextLen = contextEnd - contextStart;
         if (contextEnd <= mGapStart) {
             ret = p.getTextRunCursor(mText, contextStart, contextLen,
-                    dir, offset, cursorOpt);
+                    isRtl, offset, cursorOpt);
         } else if (contextStart >= mGapStart) {
             ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
-                    dir, offset + mGapLength, cursorOpt) - mGapLength;
+                    isRtl, offset + mGapLength, cursorOpt) - mGapLength;
         } else {
             char[] buf = TextUtils.obtain(contextLen);
             getChars(contextStart, contextEnd, buf, 0);
             ret = p.getTextRunCursor(buf, 0, contextLen,
-                    dir, offset - contextStart, cursorOpt) + contextStart;
+                    isRtl, offset - contextStart, cursorOpt) + contextStart;
             TextUtils.recycle(buf);
         }
 
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index ad7a851..b49a949 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -802,14 +802,13 @@
             }
         }
 
-        int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
         int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
         if (mCharsValid) {
             return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
-                    dir, offset, cursorOpt);
+                    runIsRtl, offset, cursorOpt);
         } else {
             return wp.getTextRunCursor(mText, mStart + spanStart,
-                    mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
+                    mStart + spanLimit, runIsRtl, mStart + offset, cursorOpt) - mStart;
         }
     }
 
diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java
index bad09f2..a0aef69 100644
--- a/core/java/android/text/TextWatcher.java
+++ b/core/java/android/text/TextWatcher.java
@@ -17,7 +17,7 @@
 package android.text;
 
 /**
- * When an object of a type is attached to an Editable, its methods will
+ * When an object of this type is attached to an Editable, its methods will
  * be called when the text is changed.
  */
 public interface TextWatcher extends NoCopySpan {
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 5f0a46d..d4bcd12 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -310,7 +310,7 @@
             return len;
         }
 
-        offset = paint.getTextRunCursor(text, offset, len, Paint.DIRECTION_LTR /* not used */,
+        offset = paint.getTextRunCursor(text, offset, len, false /* LTR, not used */,
                 offset, Paint.CURSOR_AFTER);
 
         return adjustReplacementSpan(text, offset, false /* move to the end */);
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 5210447..be47320 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -420,7 +420,7 @@
             intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode());
             context.sendBroadcast(intent);
         } else {
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = context.getSystemService(InputMethodManager.class);
             if (imm != null) {
                 imm.notifySuggestionPicked(this, original, index);
             }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c29fbbb..19e3f7f62 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7315,7 +7315,7 @@
         // Here we check whether we still need the default focus highlight, and switch it on/off.
         switchDefaultFocusHighlight();
 
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
         if (!gainFocus) {
             if (isPressed()) {
                 setPressed(false);
@@ -8523,6 +8523,11 @@
         }
         if (importance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
                 || importance == IMPORTANT_FOR_AUTOFILL_NO) {
+            if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
+                Log.v(AUTOFILL_LOG_TAG, "View (autofillId=" +  getAutofillViewId() + ", "
+                        + getClass() + ") is not important for autofill because its "
+                        + "importance is " + importance);
+            }
             return false;
         }
 
@@ -12476,7 +12481,7 @@
         mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
         onFinishTemporaryDetach();
         if (hasWindowFocus() && hasFocus()) {
-            InputMethodManager.getInstance().focusIn(this);
+            getContext().getSystemService(InputMethodManager.class).focusIn(this);
         }
         notifyEnterOrExitForAutoFillIfNeeded(true);
     }
@@ -12867,7 +12872,7 @@
      *        focus, false otherwise.
      */
     public void onWindowFocusChanged(boolean hasWindowFocus) {
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
         if (!hasWindowFocus) {
             if (isPressed()) {
                 setPressed(false);
@@ -17932,7 +17937,7 @@
         rebuildOutline();
 
         if (isFocused()) {
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
             if (imm != null) {
                 imm.focusIn(this);
             }
@@ -18515,7 +18520,7 @@
         onDetachedFromWindow();
         onDetachedFromWindowInternal();
 
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
         if (imm != null) {
             imm.onViewDetachedFromWindow(this);
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 16d202b..2ee7ab9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2579,7 +2579,7 @@
                     .mayUseInputMethod(mWindowAttributes.flags);
             if (imTarget != mLastWasImTarget) {
                 mLastWasImTarget = imTarget;
-                InputMethodManager imm = InputMethodManager.peekInstance();
+                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
                 if (imm != null && imTarget) {
                     imm.onPreWindowFocus(mView, hasWindowFocus);
                     imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2695,7 +2695,7 @@
             mLastWasImTarget = WindowManager.LayoutParams
                     .mayUseInputMethod(mWindowAttributes.flags);
 
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
             if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
                 imm.onPreWindowFocus(mView, hasWindowFocus);
             }
@@ -4329,7 +4329,7 @@
                     enqueueInputEvent(event, null, 0, true);
                 } break;
                 case MSG_CHECK_FOCUS: {
-                    InputMethodManager imm = InputMethodManager.peekInstance();
+                    InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
                     if (imm != null) {
                         imm.checkFocus();
                     }
@@ -4871,7 +4871,7 @@
         @Override
         protected int onProcess(QueuedInputEvent q) {
             if (mLastWasImTarget && !isInLocalFocusMode()) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
+                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     final InputEvent event = q.mEvent;
                     if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a7ec6df..982737a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -25,6 +25,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.WindowConfiguration;
 import android.content.Context;
@@ -1079,8 +1080,18 @@
         setFlags(flags, flags);
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Add private flag bits.
+     *
+     * <p>Refer to the individual flags for the permissions needed.
+     *
+     * <p>Note: Only for updateable system components (aka. mainline modules)
+     *
+     * @param flags The flag bits to add.
+     *
+     * @hide
+     */
+    @SystemApi
     public void addPrivateFlags(int flags) {
         setPrivateFlags(flags, flags);
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0404df0..742df5e8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1668,6 +1668,7 @@
          * this window is visible.
          * @hide
          */
+        @SystemApi
         @RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
         public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
 
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 4ca9a14..e20acf1 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -468,7 +468,7 @@
         View view = root.getView();
 
         if (view != null) {
-            InputMethodManager imm = InputMethodManager.getInstance();
+            InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
             if (imm != null) {
                 imm.windowDismissed(mViews.get(index).getWindowToken());
             }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index a3fa2ce..4d3f0fc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -632,6 +632,8 @@
 
     private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000;
 
+    private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000;
+
     /**
      * Bits that provide the id of a virtual descendant of a view.
      */
@@ -2461,6 +2463,30 @@
     }
 
     /**
+     * Returns whether node represents a text entry key that is part of a keyboard or keypad.
+     *
+     * @return {@code true} if the node is a text entry key., {@code false} otherwise.
+     */
+    public boolean isTextEntryKey() {
+        return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY);
+    }
+
+    /**
+     * Sets whether the node represents a text entry key that is part of a keyboard or keypad.
+     *
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param isTextEntryKey {@code true} if the node is a text entry key, {@code false} otherwise.
+     */
+    public void setTextEntryKey(boolean isTextEntryKey) {
+        setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey);
+    }
+
+    /**
      * Gets the package this node comes from.
      *
      * @return The package name.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9419e93..e87048e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -45,6 +45,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.KeyEvent;
@@ -559,6 +560,9 @@
             // different bridge based on which activity is currently focused
             // in the current process. Since compat would be rarely used, just
             // create and register a new instance every time.
+            if (sDebug) {
+                Slog.d(TAG, "creating CompatibilityBridge for " + mContext);
+            }
             mCompatibilityBridge = new CompatibilityBridge();
         }
     }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 508509a..be50dd1 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -57,7 +57,6 @@
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.autofill.AutofillManager;
 
-import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInputConnectionWrapper;
@@ -387,9 +386,6 @@
     final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
     final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
 
-    private final InputMethodPrivilegedOperationsRegistry mPrivOpsRegistry =
-            new InputMethodPrivilegedOperationsRegistry();
-
     // -----------------------------------------------------------
 
     static final int MSG_DUMP = 1;
@@ -665,10 +661,12 @@
     }
 
     /**
-     * Private optimization: retrieve the global InputMethodManager instance,
-     * if it exists.
+     * Private optimization: retrieve the global InputMethodManager instance, if it exists.
      * @hide
+     * @deprecated Use {@link Context#getSystemService(Class)} instead. This method cannot fully
+     *             support multi-display scenario.
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static InputMethodManager peekInstance() {
         return sInstance;
@@ -739,7 +737,7 @@
      */
     @Deprecated
     public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
-        mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
+        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
     }
 
     /**
@@ -749,7 +747,7 @@
      */
     @Deprecated
     public void hideStatusIcon(IBinder imeToken) {
-        mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
+        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
     }
 
     /** @hide */
@@ -1350,6 +1348,10 @@
                     mServedView.getWindowToken() == appWindowToken) {
                 finishInputLocked();
             }
+            if (mCurRootView != null &&
+                    mCurRootView.getWindowToken() == appWindowToken) {
+                mCurRootView = null;
+            }
         }
     }
 
@@ -1790,7 +1792,7 @@
     public void setInputMethod(IBinder token, String id) {
         if (token == null) {
             // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
             // TODO(Bug 114488811): Consider deprecating null token rule.
             try {
                 mService.setInputMethod(token, id);
@@ -1799,7 +1801,7 @@
             }
             return;
         }
-        mPrivOpsRegistry.get(token).setInputMethod(id);
+        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
     }
 
     /**
@@ -1819,7 +1821,7 @@
     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
         if (token == null) {
             // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
             // TODO(Bug 114488811): Consider deprecating null token rule.
             try {
                 mService.setInputMethodAndSubtype(token, id, subtype);
@@ -1828,7 +1830,7 @@
             }
             return;
         }
-        mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
+        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
     }
 
     /**
@@ -1848,7 +1850,7 @@
      */
     @Deprecated
     public void hideSoftInputFromInputMethod(IBinder token, int flags) {
-        mPrivOpsRegistry.get(token).hideMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
     }
 
     /**
@@ -1869,7 +1871,7 @@
      */
     @Deprecated
     public void showSoftInputFromInputMethod(IBinder token, int flags) {
-        mPrivOpsRegistry.get(token).showMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
     }
 
     /**
@@ -2229,7 +2231,7 @@
     public boolean switchToLastInputMethod(IBinder imeToken) {
         if (imeToken == null) {
             // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
             // TODO(Bug 114488811): Consider deprecating null token rule.
             try {
                 return mService.switchToPreviousInputMethod(imeToken);
@@ -2237,7 +2239,7 @@
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
     }
 
     /**
@@ -2257,7 +2259,7 @@
     public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
         if (imeToken == null) {
             // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
             // TODO(Bug 114488811): Consider deprecating null token rule.
             try {
                 return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
@@ -2265,7 +2267,8 @@
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+                .switchToNextInputMethod(onlyCurrentIme);
     }
 
     /**
@@ -2284,7 +2287,8 @@
      */
     @Deprecated
     public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
-        return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+                .shouldOfferSwitchingToNextInputMethod();
     }
 
     /**
@@ -2420,34 +2424,4 @@
         sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
         return sb.toString();
     }
-
-    /**
-     * Called by {@link InputMethodService} so that API calls to deprecated ones defined in this
-     * class can be forwarded to {@link InputMethodPrivilegedOperations}.
-     *
-     * <p>Note: this method does not hold strong references to {@code token} and {@code ops}. The
-     * registry entry will be automatically cleared after {@code token} is garbage collected.</p>
-     *
-     * @param token IME token that is associated with {@code ops}
-     * @param ops {@link InputMethodPrivilegedOperations} that is associated with {@code token}
-     * @hide
-     */
-    public void registerInputMethodPrivOps(IBinder token, InputMethodPrivilegedOperations ops) {
-        mPrivOpsRegistry.put(token, ops);
-    }
-
-    /**
-     * Called from {@link InputMethodService#onDestroy()} to make sure that deprecated IME APIs
-     * defined in this class can no longer access to {@link InputMethodPrivilegedOperations}.
-     *
-     * <p>Note: Calling this method is optional, but at least gives more explict error message in
-     * logcat when IME developers are doing something unsupported (e.g. trying to call IME APIs
-     * after {@link InputMethodService#onDestroy()}).</p>
-     *
-     * @param token IME token to be removed.
-     * @hide
-     */
-    public void unregisterInputMethodPrivOps(IBinder token) {
-        mPrivOpsRegistry.remove(token);
-    }
 }
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index cbd624e..7d6564f 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1158,7 +1158,7 @@
      * <p>Closes the drop down if present on screen.</p>
      */
     public void dismissDropDown() {
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
         if (imm != null) {
             imm.displayCompletions(this, null);
         }
@@ -1247,7 +1247,7 @@
     private void buildImeCompletions() {
         final ListAdapter adapter = mAdapter;
         if (adapter != null) {
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
             if (imm != null) {
                 final int count = Math.min(adapter.getCount(), 20);
                 CompletionInfo[] completions = new CompletionInfo[count];
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index cc8b550..e250f63 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -365,7 +365,7 @@
      * closed.
      *
      * @param newCursor The new cursor to be used.
-     * @return Returns the previously set Cursor, or null if there wasa not one.
+     * @return Returns the previously set Cursor, or null if there was not one.
      * If the given new Cursor is the same instance is the previously set
      * Cursor, null is also returned.
      */
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index f88a4e2..5f15110 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -639,7 +639,7 @@
         // changed the value via the IME and there is a next input the IME will
         // be shown, otherwise the user chose another means of changing the
         // value and having the IME up makes no sense.
-        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+        InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
         if (inputMethodManager != null) {
             if (inputMethodManager.isActive(mYearSpinnerInput)) {
                 mYearSpinnerInput.clearFocus();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4428598..8dd30f6 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1539,6 +1539,10 @@
         }
     }
 
+    private InputMethodManager getInputMethodManager() {
+        return mTextView.getContext().getSystemService(InputMethodManager.class);
+    }
+
     public void beginBatchEdit() {
         mInBatchEditControllers = true;
         final InputMethodState ims = mInputMethodState;
@@ -1707,7 +1711,7 @@
         if (req == null) {
             return false;
         }
-        final InputMethodManager imm = InputMethodManager.peekInstance();
+        final InputMethodManager imm = getInputMethodManager();
         if (imm == null) {
             return false;
         }
@@ -1742,7 +1746,7 @@
 
     private void sendUpdateSelection() {
         if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
-            final InputMethodManager imm = InputMethodManager.peekInstance();
+            final InputMethodManager imm = getInputMethodManager();
             if (null != imm) {
                 final int selectionStart = mTextView.getSelectionStart();
                 final int selectionEnd = mTextView.getSelectionEnd();
@@ -1768,7 +1772,7 @@
 
         final InputMethodState ims = mInputMethodState;
         if (ims != null && ims.mBatchEditNesting == 0) {
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = getInputMethodManager();
             if (imm != null) {
                 if (imm.isActive(mTextView)) {
                     if (ims.mContentChanged || ims.mSelectionModeChanged) {
@@ -2245,7 +2249,7 @@
                 && mTextView.isTextEditable() && !mTextView.isTextSelectable()
                 && mShowSoftInputOnFocus) {
             // Show the IME to be able to replace text, except when selecting non editable text.
-            final InputMethodManager imm = InputMethodManager.peekInstance();
+            final InputMethodManager imm = getInputMethodManager();
             if (imm != null) {
                 imm.showSoftInput(mTextView, 0, null);
             }
@@ -2255,7 +2259,7 @@
 
     private boolean extractedTextModeWillBeStarted() {
         if (!(mTextView.isInExtractedMode())) {
-            final InputMethodManager imm = InputMethodManager.peekInstance();
+            final InputMethodManager imm = getInputMethodManager();
             return  imm != null && imm.isFullscreenMode();
         }
         return false;
@@ -4272,7 +4276,7 @@
             if (ims == null || ims.mBatchEditNesting > 0) {
                 return;
             }
-            final InputMethodManager imm = InputMethodManager.peekInstance();
+            final InputMethodManager imm = getInputMethodManager();
             if (null == imm) {
                 return;
             }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b6ed22c..a28cc40 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1293,7 +1293,8 @@
      * Shows the soft input for its input text.
      */
     private void showSoftInput() {
-        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+        InputMethodManager inputMethodManager =
+                getContext().getSystemService(InputMethodManager.class);
         if (inputMethodManager != null) {
             if (mHasSelectorWheel) {
                 mInputText.setVisibility(View.VISIBLE);
@@ -1307,7 +1308,8 @@
      * Hides the soft input if it is active for the input text.
      */
     private void hideSoftInput() {
-        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+        InputMethodManager inputMethodManager =
+                getContext().getSystemService(InputMethodManager.class);
         if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
             inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
         }
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 9a24061..c78f4ac 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -268,7 +268,7 @@
      * Intent shareIntent = new Intent(Intent.ACTION_SEND);
      * shareIntent.setType("image/*");
      * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
-     * shareIntent.putExtra(Intent.EXTRA_STREAM, uri));</pre>
+     * shareIntent.putExtra(Intent.EXTRA_STREAM, uri);</pre>
      *
      * @param shareIntent The share intent.
      *
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a86e6f8..bbfac44 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2156,7 +2156,7 @@
 
         if (!enabled) {
             // Hide the soft input if the currently active TextView is disabled
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = getInputMethodManager();
             if (imm != null && imm.isActive(this)) {
                 imm.hideSoftInputFromWindow(getWindowToken(), 0);
             }
@@ -2166,7 +2166,7 @@
 
         if (enabled) {
             // Make sure IME is updated with current editor info.
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = getInputMethodManager();
             if (imm != null) imm.restartInput(this);
         }
 
@@ -2392,7 +2392,7 @@
             if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL;
         }
 
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getInputMethodManager();
         if (imm != null) imm.restartInput(this);
     }
 
@@ -5769,7 +5769,7 @@
             Editable t = mEditableFactory.newEditable(text);
             text = t;
             setFilters(t, mFilters);
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = getInputMethodManager();
             if (imm != null) imm.restartInput(this);
         } else if (precomputed != null) {
             if (mTextDir == null) {
@@ -6148,7 +6148,7 @@
             setTextInternal(removeSuggestionSpans(mText));
         }
 
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getInputMethodManager();
         if (imm != null) imm.restartInput(this);
     }
 
@@ -6436,7 +6436,7 @@
                 return;
 
             } else if (actionCode == EditorInfo.IME_ACTION_DONE) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
+                InputMethodManager imm = getInputMethodManager();
                 if (imm != null && imm.isActive(this)) {
                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
                 }
@@ -7902,7 +7902,7 @@
                     if (!hasOnClickListeners()) {
                         if (mMovement != null && mText instanceof Editable
                                 && mLayout != null && onCheckIsTextEditor()) {
-                            InputMethodManager imm = InputMethodManager.peekInstance();
+                            InputMethodManager imm = getInputMethodManager();
                             viewClicked(imm);
                             if (imm != null && getShowSoftInputOnFocus()) {
                                 imm.showSoftInput(this, 0);
@@ -7956,7 +7956,7 @@
                                     & KeyEvent.FLAG_EDITOR_ACTION) != 0) {
                                 // No target for next focus, but make sure the IME
                                 // if this came from it.
-                                InputMethodManager imm = InputMethodManager.peekInstance();
+                                InputMethodManager imm = getInputMethodManager();
                                 if (imm != null && imm.isActive(this)) {
                                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
                                 }
@@ -10260,7 +10260,7 @@
 
             if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
                 // Show the IME, except when selecting in read-only text.
-                final InputMethodManager imm = InputMethodManager.peekInstance();
+                final InputMethodManager imm = getInputMethodManager();
                 viewClicked(imm);
                 if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
                     imm.showSoftInput(this, 0);
@@ -11299,7 +11299,7 @@
         // Show the IME, except when selecting in read-only text.
         if ((mMovement != null || onCheckIsTextEditor()) && hasSpannableText() && mLayout != null
                 && (isTextEditable() || isTextSelectable()) && isFocused()) {
-            final InputMethodManager imm = InputMethodManager.peekInstance();
+            final InputMethodManager imm = getInputMethodManager();
             viewClicked(imm);
             if (!isTextSelectable() && mEditor.mShowSoftInputOnFocus && imm != null) {
                 handled |= imm.showSoftInput(this, 0);
@@ -11367,13 +11367,17 @@
         sendAccessibilityEventUnchecked(event);
     }
 
+    private InputMethodManager getInputMethodManager() {
+        return getContext().getSystemService(InputMethodManager.class);
+    }
+
     /**
      * Returns whether this text view is a current input method target.  The
      * default implementation just checks with {@link InputMethodManager}.
      * @return True if the TextView is a current input method target; false otherwise.
      */
     public boolean isInputMethodTarget() {
-        InputMethodManager imm = InputMethodManager.peekInstance();
+        InputMethodManager imm = getInputMethodManager();
         return imm != null && imm.isActive(this);
     }
 
@@ -12491,11 +12495,11 @@
                     advancesIndex);
         }
 
-        public int getTextRunCursor(int contextStart, int contextEnd, int dir,
+        public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl,
                 int offset, int cursorOpt, Paint p) {
             int contextCount = contextEnd - contextStart;
             return p.getTextRunCursor(mChars, contextStart + mStart,
-                    contextCount, dir, offset + mStart, cursorOpt);
+                    contextCount, isRtl, offset + mStart, cursorOpt);
         }
     }
 
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 77670b3..6a3a8f0 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -269,7 +269,7 @@
             mRadialTimePickerModeButton.setContentDescription(
                     mTextInputPickerModeEnabledDescription);
             updateTextInputPicker();
-            InputMethodManager imm = InputMethodManager.peekInstance();
+            InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
             if (imm != null) {
                 imm.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
             }
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index cc79b9c..83c86d5 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -471,7 +471,7 @@
         // changed the value via the IME and there is a next input the IME will
         // be shown, otherwise the user chose another means of changing the
         // value and having the IME up makes no sense.
-        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+        InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
         if (inputMethodManager != null) {
             if (inputMethodManager.isActive(mHourSpinnerInput)) {
                 mHourSpinnerInput.clearFocus();
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 9171959..0f8295a 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -16,8 +16,7 @@
 
 package com.android.internal.app;
 
-import com.android.internal.R;
-
+import android.annotation.NonNull;
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -32,6 +31,9 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Set;
+
 /**
  * Utility method for dealing with the assistant aspects of
  * {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
@@ -62,6 +64,30 @@
         return false;
     }
 
+    /**
+     * Checks the availability of a set of voice actions for the current active voice service.
+     *
+     * @param voiceActions A set of supported voice actions to be checked.
+     * @param callback     The callback which will deliver a set of supported voice actions. If
+     *                     no voice actions are supported for the given voice action set, then null
+     *                     or empty set is provided.
+     */
+    public void getActiveServiceSupportedActions(@NonNull Set<String> voiceActions,
+            @NonNull IVoiceActionCheckCallback callback) {
+        try {
+            if (mVoiceInteractionManagerService != null) {
+                mVoiceInteractionManagerService
+                        .getActiveServiceSupportedActions(new ArrayList<>(voiceActions), callback);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to call activeServiceSupportedActions", e);
+            try {
+                callback.onComplete(null);
+            } catch (RemoteException re) {
+            }
+        }
+    }
+
     public void launchVoiceAssistFromKeyguard() {
         try {
             if (mVoiceInteractionManagerService != null) {
@@ -157,7 +183,7 @@
             return getActiveServiceComponentName();
         }
         final SearchManager searchManager =
-            (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+                (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
         if (searchManager == null) {
             return null;
         }
diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
similarity index 63%
rename from packages/SystemUI/res/values-h600dp/config.xml
rename to core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
index 8616e3e..66ba93d 100644
--- a/packages/SystemUI/res/values-h600dp/config.xml
+++ b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
@@ -1,22 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2017, 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.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT 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>
-    <!-- The number of rows in the QuickSettings -->
-    <integer name="quick_settings_num_rows">3</integer>
-</resources>
+package com.android.internal.app;
+
+oneway interface IVoiceActionCheckCallback {
+    void onComplete(in List<String> voiceActions);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ff75a8b..5088cca 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -143,4 +144,11 @@
      * Register a voice interaction listener.
      */
     void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
+
+    /**
+     * Checks the availability of a set of voice actions for the current active voice service.
+     * Returns all supported voice actions.
+     */
+    void getActiveServiceSupportedActions(in List<String> voiceActions,
+     in IVoiceActionCheckCallback callback);
 }
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b33a5c4..81dab2f 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -232,6 +232,7 @@
         displayName = FileUtils.buildValidFatFilename(displayName);
 
         final File before = getFileForDocId(docId);
+        final File beforeVisibleFile = getFileForDocId(docId, true);
         final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
         if (!before.renameTo(after)) {
             throw new IllegalStateException("Failed to rename to " + after);
@@ -241,7 +242,6 @@
         onDocIdChanged(docId);
         onDocIdChanged(afterDocId);
 
-        final File beforeVisibleFile = getFileForDocId(docId, true);
         final File afterVisibleFile = getFileForDocId(afterDocId, true);
         moveInMediaStore(beforeVisibleFile, afterVisibleFile);
 
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
index 3255153..1436aed 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
@@ -29,12 +29,19 @@
 /**
  * A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
  * used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
+ *
+ * <p>This class is designed to be used as a per-process global registry.</p>
  */
 public final class InputMethodPrivilegedOperationsRegistry {
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
-            mRegistry = new WeakHashMap<>();
+    private InputMethodPrivilegedOperationsRegistry() {
+        // Not intended to be instantiated.
+    }
+
+    private static final Object sLock = new Object();
+
+    @Nullable
+    @GuardedBy("sLock")
+    private static WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>> sRegistry;
 
     @Nullable
     private static InputMethodPrivilegedOperations sNop;
@@ -62,10 +69,13 @@
      * @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
      */
     @AnyThread
-    public void put(IBinder token, InputMethodPrivilegedOperations ops) {
-        synchronized (mLock) {
+    public static void put(IBinder token, InputMethodPrivilegedOperations ops) {
+        synchronized (sLock) {
+            if (sRegistry == null) {
+                sRegistry = new WeakHashMap<>();
+            }
             final WeakReference<InputMethodPrivilegedOperations> previousOps =
-                    mRegistry.put(token, new WeakReference<>(ops));
+                    sRegistry.put(token, new WeakReference<>(ops));
             if (previousOps != null) {
                 throw new IllegalStateException(previousOps.get() + " is already registered for "
                         + " this token=" + token + " newOps=" + ops);
@@ -84,9 +94,12 @@
      */
     @NonNull
     @AnyThread
-    public InputMethodPrivilegedOperations get(IBinder token) {
-        synchronized (mLock) {
-            final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
+    public static InputMethodPrivilegedOperations get(IBinder token) {
+        synchronized (sLock) {
+            if (sRegistry == null) {
+                return getNopOps();
+            }
+            final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token);
             if (wrapperRef == null) {
                 return getNopOps();
             }
@@ -108,9 +121,15 @@
      * @param token IME token to be removed.
      */
     @AnyThread
-    public void remove(IBinder token) {
-        synchronized (mLock) {
-            mRegistry.remove(token);
+    public static void remove(IBinder token) {
+        synchronized (sLock) {
+            if (sRegistry == null) {
+                return;
+            }
+            sRegistry.remove(token);
+            if (sRegistry.isEmpty()) {
+                sRegistry = null;
+            }
         }
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d0795c9..31f13c3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5758,8 +5758,6 @@
         for (int i = 0; i < N; i++) {
             int uid = mapUid(ws.get(i));
             getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
-            StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
-                    numNewResults);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -5768,8 +5766,6 @@
                 final WorkChain wc = workChains.get(i);
                 int uid = mapUid(wc.getAttributionUid());
                 getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
-                StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED,
-                        wc.getUids(), wc.getTags(), numNewResults);
             }
         }
     }
diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java
index 396deb4..c233ea8 100644
--- a/core/java/com/android/internal/os/KernelCpuProcReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcReader.java
@@ -24,13 +24,14 @@
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
+import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
 
 /**
  * Reads cpu time proc files with throttling (adjustable interval).
@@ -55,7 +56,6 @@
     private static final int ERROR_THRESHOLD = 5;
     // Throttle interval in milliseconds
     private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
-    private static final int INITIAL_BUFFER_SIZE = 8 * 1024;
     private static final int MAX_BUFFER_SIZE = 1024 * 1024;
     private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
     private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
@@ -84,13 +84,12 @@
     private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
     private long mLastReadTime = Long.MIN_VALUE;
     private final Path mProc;
-    private ByteBuffer mBuffer;
+    private byte[] mBuffer = new byte[8 * 1024];
+    private int mContentSize;
 
     @VisibleForTesting
     public KernelCpuProcReader(String procFile) {
         mProc = Paths.get(procFile);
-        mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE);
-        mBuffer.clear();
     }
 
     /**
@@ -108,38 +107,45 @@
             return null;
         }
         if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
-            if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) {
-                // mBuffer has data.
-                return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+            if (mContentSize > 0) {
+                return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
+                        .order(ByteOrder.nativeOrder());
             }
             return null;
         }
         mLastReadTime = SystemClock.elapsedRealtime();
-        mBuffer.clear();
+        mContentSize = 0;
         final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) {
-            while (fc.read(mBuffer) == mBuffer.capacity()) {
-                if (!resize()) {
-                    mErrors++;
-                    Slog.e(TAG, "Proc file is too large: " + mProc);
-                    return null;
+        try (InputStream in = Files.newInputStream(mProc)) {
+            int numBytes = 0;
+            int curr;
+            while ((curr = in.read(mBuffer, numBytes, mBuffer.length - numBytes)) >= 0) {
+                numBytes += curr;
+                if (numBytes == mBuffer.length) {
+                    // Hit the limit. Resize mBuffer.
+                    if (mBuffer.length == MAX_BUFFER_SIZE) {
+                        mErrors++;
+                        Slog.e(TAG, "Proc file is too large: " + mProc);
+                        return null;
+                    }
+                    mBuffer = Arrays.copyOf(mBuffer,
+                            Math.min(mBuffer.length << 1, MAX_BUFFER_SIZE));
                 }
-                fc.position(0);
             }
+            mContentSize = numBytes;
+            return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
+                    .order(ByteOrder.nativeOrder());
         } catch (NoSuchFileException | FileNotFoundException e) {
             // Happens when the kernel does not provide this file. Not a big issue. Just log it.
             mErrors++;
             Slog.w(TAG, "File not exist: " + mProc);
-            return null;
         } catch (IOException e) {
             mErrors++;
             Slog.e(TAG, "Error reading: " + mProc, e);
-            return null;
         } finally {
             StrictMode.setThreadPolicyMask(oldMask);
         }
-        mBuffer.flip();
-        return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+        return null;
     }
 
     /**
@@ -153,14 +159,4 @@
             mThrottleInterval = throttleInterval;
         }
     }
-
-    private boolean resize() {
-        if (mBuffer.capacity() >= MAX_BUFFER_SIZE) {
-            return false;
-        }
-        int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE);
-        // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize);
-        mBuffer = ByteBuffer.allocateDirect(newSize);
-        return true;
-    }
 }
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 7548c22..101fd41 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -139,8 +139,7 @@
          * The client should try to restart input when its {@link android.view.Window} is focused
          * again.</p>
          *
-         * @see com.android.server.wm.WindowManagerInternal#inputMethodClientHasFocus(
-         * IInputMethodClient)
+         * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
          */
         int ERROR_NOT_IME_TARGET_WINDOW = 11;
         /**
diff --git a/packages/SettingsLib/res/drawable/ic_info.xml b/core/res/res/drawable/ic_info.xml
similarity index 94%
rename from packages/SettingsLib/res/drawable/ic_info.xml
rename to core/res/res/drawable/ic_info.xml
index afe7e6b..f14c4b4 100644
--- a/packages/SettingsLib/res/drawable/ic_info.xml
+++ b/core/res/res/drawable/ic_info.xml
@@ -18,7 +18,7 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?android:attr/colorAccent">
+        android:tint="?attr/colorControlNormal">
     <path
         android:fillColor="@android:color/white"
         android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
diff --git a/core/res/res/values-mcc262-mnc02/strings.xml b/core/res/res/values-mcc262-mnc02/strings.xml
deleted file mode 100644
index 2b89401..0000000
--- a/core/res/res/values-mcc262-mnc02/strings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2017, Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-     <!-- Do not translate. Template for showing mobile network operator name while WFC is active -->
-     <string-array name="wfcSpnFormats">
-         <item>%s</item>
-         <item>%s Wi-Fi Calling</item>
-     </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc370/strings.xml b/core/res/res/values-mcc302-mnc370/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc370/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Template for showing mobile network operator name while WFC is active -->
-    <string-array name="wfcSpnFormats">
-        <item>%s</item>
-        <item>%s Wi-Fi</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc720/strings.xml b/core/res/res/values-mcc302-mnc720/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc720/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Template for showing mobile network operator name while WFC is active -->
-    <string-array name="wfcSpnFormats">
-        <item>%s</item>
-        <item>%s Wi-Fi</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c4f68e3..1404383 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3486,6 +3486,9 @@
     <!-- Name of the font family used for system surfaces where the font should use medium weight -->
     <string name="config_headlineFontFamilyMedium">@string/font_family_button_material</string>
 
+    <!-- Size of icon shown beside a preference locked by admin -->
+    <dimen name="config_restricted_icon_size">@dimen/restricted_icon_size_material</dimen>
+
     <string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
 
     <!-- Package name that should be granted Notification Assistant access -->
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 210f30e..a0b40ed 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -117,6 +117,9 @@
     <!-- Default rounded corner for controls -->
     <dimen name="control_corner_material">2dp</dimen>
 
+    <!-- Size of icon shown beside a preference locked by admin -->
+    <dimen name="restricted_icon_size_material">16dp</dimen>
+
     <dimen name="edit_text_inset_horizontal_material">4dp</dimen>
     <dimen name="edit_text_inset_top_material">10dp</dimen>
     <dimen name="edit_text_inset_bottom_material">7dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c751af3..cc99a4e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,14 +2909,16 @@
         <public name="opticalInsetBottom" />
         <public name="allowForceDark" />
         <public name="supportsAmbientMode" />
-    </public-group>
-
-    <public-group type="attr" first-id="0x0101058d">
         <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
         <public name="usesNonSdkApi" />
         <public name="minimumUiTimeout" />
     </public-group>
 
+    <public-group type="drawable" first-id="0x010800b4">
+        <!-- @hide @SystemApi -->
+        <public name="ic_info" />
+    </public-group>
+
     <public-group type="style" first-id="0x010302e2">
     </public-group>
 
@@ -2943,6 +2945,11 @@
         <public name="config_sendPackageName" />
     </public-group>
 
+    <public-group type="dimen" first-id="0x01050007">
+        <!-- @hide @SystemApi -->
+        <public name="config_restricted_icon_size" />
+    </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 1a5b7b6..64620f3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -240,10 +240,31 @@
         <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
     </string-array>
     <!-- Template for showing mobile network operator name while WFC is active -->
-    <string-array name="wfcSpnFormats">
-        <item>%s</item>
-        <item>%s Wi-Fi Calling</item>
+    <string-array name="wfcSpnFormats" translatable="false">
+        <item>@string/wfcSpnFormat_spn</item>
+        <item>@string/wfcSpnFormat_spn_wifi_calling</item>
+        <item>@string/wfcSpnFormat_wlan_call</item>
+        <item>@string/wfcSpnFormat_spn_wlan_call</item>
+        <item>@string/wfcSpnFormat_spn_wifi</item>
+        <item>@string/wfcSpnFormat_wifi_calling_bar_spn</item>
+        <item>@string/wfcSpnFormat_spn_vowifi</item>
     </string-array>
+
+    <!-- Spn during Wi-Fi Calling: "<operator>" -->
+    <string name="wfcSpnFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
+    <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi Calling" -->
+    <string name="wfcSpnFormat_spn_wifi_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi Calling</string>
+    <!-- Spn during Wi-Fi Calling: "WLAN Call" -->
+    <string name="wfcSpnFormat_wlan_call">WLAN Call</string>
+    <!-- Spn during Wi-Fi Calling: "<operator> WLAN Call" -->
+    <string name="wfcSpnFormat_spn_wlan_call"><xliff:g id="spn" example="Operator">%s</xliff:g> WLAN Call</string>
+    <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi" -->
+    <string name="wfcSpnFormat_spn_wifi"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi</string>
+    <!-- Spn during Wi-Fi Calling: "WiFi Calling | <operator>" -->
+    <string name="wfcSpnFormat_wifi_calling_bar_spn">WiFi Calling | <xliff:g id="spn" example="Operator">%s</xliff:g></string>
+    <!-- Spn during Wi-Fi Calling: "<operator> VoWifi" -->
+    <string name="wfcSpnFormat_spn_vowifi"><xliff:g id="spn" example="Operator">%s</xliff:g> VoWifi</string>
+
     <!-- WFC, summary for Disabled -->
     <string name="wifi_calling_off_summary">Off</string>
     <!-- WFC, summary for Wi-Fi Preferred -->
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 922b79a..69d2828 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -57,7 +57,7 @@
 
     // The number of flags held in boolean properties. Their values should also be double-checked
     // in the methods above.
-    private static final int NUM_BOOLEAN_PROPERTIES = 17;
+    private static final int NUM_BOOLEAN_PROPERTIES = 18;
 
     @Test
     public void testStandardActions_serializationFlagIsValid() {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c4017d1..73c10d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -210,6 +210,11 @@
     <allow-in-power-save-except-idle package="com.android.providers.calendar" />
     <allow-in-power-save-except-idle package="com.android.providers.contacts" />
 
+    <!-- The PAC proxy process must have network access, otherwise no app will
+         be able to connect to the internet when such a proxy is in use, since
+         all outgoing connections originate from this app. -->
+    <allow-in-power-save-except-idle package="com.android.proxyhandler" />
+
     <!-- These are the packages that are white-listed to be able to run as system user -->
     <system-user-whitelisted-app package="com.android.settings" />
 
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 522db3c..b463dd8 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -21,8 +21,6 @@
 key 0x126    DPAD_DOWN
 key 0x127    DPAD_LEFT
 
-key 0x120    BUTTON_SELECT
-key 0x123    BUTTON_START
 key 0x12e    BUTTON_A
 key 0x12d    BUTTON_B
 key 0x12f    BUTTON_X
@@ -34,9 +32,6 @@
 key 0x121    BUTTON_THUMBL
 key 0x122    BUTTON_THUMBR
 
-# PS key
-key 0x2d0    BUTTON_MODE
-
 # Left Analog Stick
 axis 0x00    X
 axis 0x01    Y
@@ -74,3 +69,11 @@
 
 # Square
 # axis 0x37
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x120    BUTTON_SELECT
+# Start
+key 0x123    BUTTON_START
+# PS key
+key 0x2d0    BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+#   and when connected over Bluetooth
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+#   and when connected over Bluetooth
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
new file mode 100644
index 0000000..5fe35f7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when
+#   connected over USB
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -60,7 +60,6 @@
 key 0x138    BUTTON_SELECT
 # Options
 key 0x139    BUTTON_START
-
 # PS key
 key 0x13c    BUTTON_MODE
 
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+#   connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -60,7 +60,6 @@
 key 0x138    BUTTON_SELECT
 # Options
 key 0x139    BUTTON_START
-
 # PS key
 key 0x13c    BUTTON_MODE
 
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+#   connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_0ba0.kl b/data/keyboards/Vendor_054c_Product_0ba0.kl
new file mode 100644
index 0000000..bc6fc3b
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0.kl
@@ -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.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# L2 axis
+axis 0x03   LTRIGGER
+# R2 axis
+axis 0x04   RTRIGGER
+
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c    BUTTON_MODE
+
+# Touchpad press
+# The touchpad for this joystick will become a separate input device in future releases
+# and this button will be equivalent to left mouse button
+# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
+key 0x13d   BUTTON_1
diff --git a/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
new file mode 100644
index 0000000..8b85a38
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
@@ -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.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+# - Version 0x8111 is for Linux hid-sony driver >=4.10
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 85b39fe..33caa00 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +38,8 @@
 
 import libcore.util.NativeAllocationRegistry;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -309,38 +312,47 @@
      */
     public static final int DIRECTION_RTL = 1;
 
+    /** @hide */
+    @IntDef(prefix = { "CURSOR_" }, value = {
+        CURSOR_AFTER, CURSOR_AT_OR_AFTER, CURSOR_BEFORE, CURSOR_AT_OR_BEFORE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CursorOption {}
+
     /**
-     * Option for getTextRunCursor to compute the valid cursor after
-     * offset or the limit of the context, whichever is less.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor after offset or the limit of the context, whichever is less.
      */
     public static final int CURSOR_AFTER = 0;
 
     /**
-     * Option for getTextRunCursor to compute the valid cursor at or after
-     * the offset or the limit of the context, whichever is less.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor at or after the offset or the limit of the context, whichever is
+     * less.
      */
     public static final int CURSOR_AT_OR_AFTER = 1;
 
      /**
-     * Option for getTextRunCursor to compute the valid cursor before
-     * offset or the start of the context, whichever is greater.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor before offset or the start of the context, whichever is greater.
      */
     public static final int CURSOR_BEFORE = 2;
 
    /**
-     * Option for getTextRunCursor to compute the valid cursor at or before
-     * offset or the start of the context, whichever is greater.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor at or before offset or the start of the context, whichever is
+     * greater.
      */
     public static final int CURSOR_AT_OR_BEFORE = 3;
 
     /**
-     * Option for getTextRunCursor to return offset if the cursor at offset
-     * is valid, or -1 if it isn't.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Return offset if the cursor at offset is valid, or -1 if it isn't.
      */
     public static final int CURSOR_AT = 4;
 
@@ -2410,34 +2422,32 @@
     }
 
     /**
-     * Returns the next cursor position in the run.  This avoids placing the
-     * cursor between surrogates, between characters that form conjuncts,
-     * between base characters and combining marks, or within a reordering
-     * cluster.
+     * Returns the next cursor position in the run.
      *
-     * <p>ContextStart and offset are relative to the start of text.
-     * The context is the shaping context for cursor movement, generally
-     * the bounds of the metric span enclosing the cursor in the direction of
-     * movement.
+     * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering cluster.
      *
-     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
-     * cursor position, this returns -1.  Otherwise this will never return a
-     * value before contextStart or after contextStart + contextLength.
+     * <p>
+     * ContextStart and offset are relative to the start of text.
+     * The context is the shaping context for cursor movement, generally the bounds of the metric
+     * span enclosing the cursor in the direction of movement.
+     *
+     * <p>
+     * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+     * returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextStart + contextLength.
      *
      * @param text the text
      * @param contextStart the start of the context
      * @param contextLength the length of the context
-     * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param isRtl true if the paragraph context is RTL, otherwise false
      * @param offset the cursor position to move from
-     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
-     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
-     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @param cursorOpt how to move the cursor
      * @return the offset of the next position, or -1
-     * @hide
      */
-    @UnsupportedAppUsage
-    public int getTextRunCursor(char[] text, int contextStart, int contextLength,
-            int dir, int offset, int cursorOpt) {
+    public int getTextRunCursor(@NonNull char[] text, @IntRange(from = 0) int contextStart,
+            @IntRange(from = 0) int contextLength, boolean isRtl, @IntRange(from = 0) int offset,
+            @CursorOption int cursorOpt) {
         int contextEnd = contextStart + contextLength;
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
@@ -2446,85 +2456,87 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength, dir, offset,
-                cursorOpt);
+        return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength,
+                isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
     }
 
     /**
-     * Returns the next cursor position in the run.  This avoids placing the
-     * cursor between surrogates, between characters that form conjuncts,
-     * between base characters and combining marks, or within a reordering
-     * cluster.
+     * Returns the next cursor position in the run.
      *
-     * <p>ContextStart, contextEnd, and offset are relative to the start of
+     * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering cluster.
+     *
+     * <p>
+     * ContextStart, contextEnd, and offset are relative to the start of
      * text.  The context is the shaping context for cursor movement, generally
      * the bounds of the metric span enclosing the cursor in the direction of
      * movement.
      *
-     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
-     * cursor position, this returns -1.  Otherwise this will never return a
-     * value before contextStart or after contextEnd.
+     * <p>
+     * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+     * returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextEnd.
      *
      * @param text the text
      * @param contextStart the start of the context
      * @param contextEnd the end of the context
-     * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param isRtl true if the paragraph context is RTL, otherwise false
      * @param offset the cursor position to move from
-     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
-     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
-     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @param cursorOpt how to move the cursor
      * @return the offset of the next position, or -1
-     * @hide
      */
-    public int getTextRunCursor(CharSequence text, int contextStart,
-           int contextEnd, int dir, int offset, int cursorOpt) {
+    public int getTextRunCursor(@NonNull CharSequence text, @IntRange(from = 0) int contextStart,
+            @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+            @CursorOption int cursorOpt) {
 
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             return getTextRunCursor(text.toString(), contextStart, contextEnd,
-                    dir, offset, cursorOpt);
+                    isRtl, offset, cursorOpt);
         }
         if (text instanceof GraphicsOperations) {
             return ((GraphicsOperations) text).getTextRunCursor(
-                    contextStart, contextEnd, dir, offset, cursorOpt, this);
+                    contextStart, contextEnd, isRtl, offset, cursorOpt, this);
         }
 
         int contextLen = contextEnd - contextStart;
         char[] buf = TemporaryBuffer.obtain(contextLen);
         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
-        int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
+        int relPos = getTextRunCursor(buf, 0, contextLen, isRtl, offset - contextStart, cursorOpt);
         TemporaryBuffer.recycle(buf);
         return (relPos == -1) ? -1 : relPos + contextStart;
     }
 
     /**
-     * Returns the next cursor position in the run.  This avoids placing the
-     * cursor between surrogates, between characters that form conjuncts,
-     * between base characters and combining marks, or within a reordering
-     * cluster.
+     * Returns the next cursor position in the run.
      *
-     * <p>ContextStart, contextEnd, and offset are relative to the start of
-     * text.  The context is the shaping context for cursor movement, generally
-     * the bounds of the metric span enclosing the cursor in the direction of
-     * movement.
+     * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering cluster.
      *
-     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
-     * cursor position, this returns -1.  Otherwise this will never return a
-     * value before contextStart or after contextEnd.
+     * <p>
+     * ContextStart, contextEnd, and offset are relative to the start of text.  The context is the
+     * shaping context for cursor movement, generally the bounds of the metric span enclosing the
+     * cursor in the direction of movement.
+     * </p>
+     *
+     * <p>
+     * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+     * returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextEnd.
+     * </p>
      *
      * @param text the text
      * @param contextStart the start of the context
      * @param contextEnd the end of the context
-     * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param isRtl true if the paragraph context is RTL, otherwise false.
      * @param offset the cursor position to move from
-     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
-     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
-     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @param cursorOpt how to move the cursor
      * @return the offset of the next position, or -1
      * @hide
      */
-    public int getTextRunCursor(String text, int contextStart, int contextEnd,
-            int dir, int offset, int cursorOpt) {
+    public int getTextRunCursor(@NonNull String text, @IntRange(from = 0) int contextStart,
+            @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+            @CursorOption int cursorOpt) {
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
                 | (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2532,8 +2544,8 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd, dir, offset,
-                cursorOpt);
+        return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd,
+                isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
     }
 
     /**
@@ -2599,11 +2611,13 @@
      * Return in bounds (allocated by the caller) the smallest rectangle that
      * encloses all of the characters, with an implied origin at (0,0).
      *
+     * Note that styles are ignored even if you pass {@link android.text.Spanned} instance.
+     * Use {@link android.text.StaticLayout} for measuring bounds of {@link android.text.Spanned}.
+     *
      * @param text text to measure and return its bounds
      * @param start index of the first char in the text to measure
      * @param end 1 past the last char in the text to measure
      * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
-     * @hide
      */
     public void getTextBounds(CharSequence text, int start, int end, Rect bounds) {
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b011363..11dad2e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -175,6 +175,7 @@
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/SkiaVulkanPipeline.cpp",
         "pipeline/skia/VectorDrawableAtlas.cpp",
+        "pipeline/skia/VkFunctorDrawable.cpp",
         "renderstate/RenderState.cpp",
         "renderthread/CacheManager.cpp",
         "renderthread/CanvasContext.cpp",
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index b5b87d5..21fbbdc 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -26,8 +26,6 @@
 
 #include <log/log.h>
 
-#include <GLES2/gl2.h>
-
 namespace android {
 namespace uirenderer {
 
@@ -46,39 +44,12 @@
         1920,   // viewportH
 };
 
-static DeviceInfo* sDeviceInfo = nullptr;
-static std::once_flag sInitializedFlag;
-
 const DeviceInfo* DeviceInfo::get() {
-    LOG_ALWAYS_FATAL_IF(!sDeviceInfo, "DeviceInfo not yet initialized.");
-    return sDeviceInfo;
+        static DeviceInfo sDeviceInfo;
+        return &sDeviceInfo;
 }
 
-void DeviceInfo::initialize() {
-    std::call_once(sInitializedFlag, []() {
-        sDeviceInfo = new DeviceInfo();
-        sDeviceInfo->load();
-    });
-}
-
-void DeviceInfo::initialize(int maxTextureSize) {
-    std::call_once(sInitializedFlag, [maxTextureSize]() {
-        sDeviceInfo = new DeviceInfo();
-        sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo();
-        sDeviceInfo->mMaxTextureSize = maxTextureSize;
-        sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace,
-                                                &sDeviceInfo->mTargetPixelFormat);
-    });
-}
-
-void DeviceInfo::load() {
-    mDisplayInfo = queryDisplayInfo();
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace,
-                                            &sDeviceInfo->mTargetPixelFormat);
-}
-
-DisplayInfo DeviceInfo::queryDisplayInfo() {
+DisplayInfo QueryDisplayInfo() {
     if (Properties::isolatedProcess) {
         return sDummyDisplay;
     }
@@ -90,17 +61,36 @@
     return displayInfo;
 }
 
-void DeviceInfo::queryCompositionPreference(ui::Dataspace* dataSpace,
-                                            ui::PixelFormat* pixelFormat) {
+void QueryCompositionPreference(ui::Dataspace* dataSpace,
+                                ui::PixelFormat* pixelFormat) {
     if (Properties::isolatedProcess) {
         *dataSpace = ui::Dataspace::V0_SRGB;
         *pixelFormat = ui::PixelFormat::RGBA_8888;
     }
 
     status_t status =
-        SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat);
+            SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat);
     LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status);
 }
 
+DeviceInfo::DeviceInfo() {
+#if HWUI_NULL_GPU
+        mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
+#else
+        mMaxTextureSize = -1;
+#endif
+    mDisplayInfo = QueryDisplayInfo();
+    QueryCompositionPreference(&mTargetDataSpace, &mTargetPixelFormat);
+}
+
+int DeviceInfo::maxTextureSize() const {
+    LOG_ALWAYS_FATAL_IF(mMaxTextureSize < 0, "MaxTextureSize has not been initialized yet.");
+    return mMaxTextureSize;
+}
+
+void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
+    const_cast<DeviceInfo*>(DeviceInfo::get())->mMaxTextureSize = maxTextureSize;
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 416af17..1d747741 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -19,51 +19,38 @@
 #include <ui/DisplayInfo.h>
 #include <ui/GraphicTypes.h>
 
-#include "Extensions.h"
 #include "utils/Macros.h"
 
 namespace android {
 namespace uirenderer {
 
+namespace renderthread {
+    class RenderThread;
+}
+
 class DeviceInfo {
     PREVENT_COPY_AND_ASSIGN(DeviceInfo);
 
 public:
-    // returns nullptr if DeviceInfo is not initialized yet
-    // Note this does not have a memory fence so it's up to the caller
-    // to use one if required. Normally this should not be necessary
     static const DeviceInfo* get();
 
-    // only call this after GL has been initialized, or at any point if compiled
-    // with HWUI_NULL_GPU
-    static void initialize();
-    static void initialize(int maxTextureSize);
+    // this value is only valid after the GPU has been initialized and there is a valid graphics
+    // context or if you are using the HWUI_NULL_GPU
+    int maxTextureSize() const;
 
-    int maxTextureSize() const { return mMaxTextureSize; }
     ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; }
     ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; }
     const DisplayInfo& displayInfo() const { return mDisplayInfo; }
-    const Extensions& extensions() const { return mExtensions; }
-
-    static uint32_t multiplyByResolution(uint32_t in) {
-        auto di = DeviceInfo::get()->displayInfo();
-        return di.w * di.h * in;
-    }
-
-    static DisplayInfo queryDisplayInfo();
 
 private:
-    static void queryCompositionPreference(ui::Dataspace* dataSpace,
-                                           ui::PixelFormat* pixelFormat);
+    friend class renderthread::RenderThread;
+    static void setMaxTextureSize(int maxTextureSize);
 
-    DeviceInfo() {}
-    ~DeviceInfo() {}
-
-    void load();
+    DeviceInfo();
 
     int mMaxTextureSize;
     DisplayInfo mDisplayInfo;
-    Extensions mExtensions;
+
     // TODO(lpy) Replace below with android_ prefix types.
     ui::Dataspace mTargetDataSpace;
     ui::PixelFormat mTargetPixelFormat;
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
deleted file mode 100644
index e90f40c..0000000
--- a/libs/hwui/Extensions.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_EXTENSIONS_H
-#define ANDROID_HWUI_EXTENSIONS_H
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-class Extensions {
-public:
-    Extensions() {}
-
-    inline bool hasNPot() const { return false; }
-    inline bool hasFramebufferFetch() const { return false; }
-    inline bool hasDiscardFramebuffer() const { return false; }
-    inline bool hasDebugMarker() const { return false; }
-    inline bool has1BitStencil() const { return false; }
-    inline bool has4BitStencil() const { return false; }
-    inline bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
-    inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
-    inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
-    inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
-    inline bool hasRenderableFloatTextures() const {
-        return (mVersionMajor >= 3 && mVersionMinor >= 2);
-    }
-    inline bool hasSRGB() const { return false; }
-    inline bool hasSRGBWriteControl() const { return hasSRGB() && false; }
-    inline bool hasLinearBlending() const { return hasSRGB() && false; }
-
-    inline int getMajorGlVersion() const { return mVersionMajor; }
-    inline int getMinorGlVersion() const { return mVersionMinor; }
-
-private:
-    int mVersionMajor = 2;
-    int mVersionMinor = 0;
-};  // class Extensions
-
-};  // namespace uirenderer
-};  // namespace android
-
-#endif  // ANDROID_HWUI_EXTENSIONS_H
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index ab80d3d..165fc48 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -45,7 +45,7 @@
 }
 
 static void checkIdleTimeout() {
-    std::lock_guard{sLock};
+    std::lock_guard _lock{sLock};
     if (sPendingUploads == 0 && shouldTimeOutLocked()) {
         sEglManager.destroy();
     } else {
@@ -54,7 +54,7 @@
 }
 
 static void beginUpload() {
-    std::lock_guard{sLock};
+    std::lock_guard _lock{sLock};
     sPendingUploads++;
 
     if (!sUploadThread) {
@@ -75,13 +75,13 @@
 }
 
 static void endUpload() {
-    std::lock_guard{sLock};
+    std::lock_guard _lock{sLock};
     sPendingUploads--;
     sLastUpload = systemTime();
 }
 
 static EGLDisplay getUploadEglDisplay() {
-    std::lock_guard{sLock};
+    std::lock_guard _lock{sLock};
     LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
     return sEglManager.eglDisplay();
 }
@@ -107,39 +107,6 @@
 
 #define FENCE_TIMEOUT 2000000000
 
-class AutoEglImage {
-public:
-    AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
-        EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
-        image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
-                                  imageAttrs);
-    }
-
-    ~AutoEglImage() {
-        if (image != EGL_NO_IMAGE_KHR) {
-            eglDestroyImageKHR(mDisplay, image);
-        }
-    }
-
-    EGLImageKHR image = EGL_NO_IMAGE_KHR;
-
-private:
-    EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
-class AutoSkiaGlTexture {
-public:
-    AutoSkiaGlTexture() {
-        glGenTextures(1, &mTexture);
-        glBindTexture(GL_TEXTURE_2D, mTexture);
-    }
-
-    ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
-
-private:
-    GLuint mTexture = 0;
-};
-
 struct FormatInfo {
     PixelFormat pixelFormat;
     GLint format, type;
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e6d2a6f..f2d50cd 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -146,7 +146,7 @@
                              frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
 
     // If we hit the deadline, cool!
-    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline || totalDuration < mFrameInterval) {
         if (isTripleBuffered) {
             mData->reportJankType(JankType::kHighInputLatency);
             (*mGlobalData)->reportJankType(JankType::kHighInputLatency);
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
index 27a29aa..3da6e80 100644
--- a/libs/hwui/debug/wrap_gles.h
+++ b/libs/hwui/debug/wrap_gles.h
@@ -28,6 +28,9 @@
 #include <GLES3/gl31.h>
 #include <GLES3/gl32.h>
 
+// constant used by the NULL GPU implementation as well as HWUI's unit tests
+constexpr int NULL_GPU_MAX_TEXTURE_SIZE = 2048;
+
 // Generate stubs that route all the calls to our function table
 #include "gles_redefine.h"
 
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index dcfe6b3..e4ba13d 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -138,7 +138,7 @@
             renderNodeDrawable->getRenderNode()->output(mOutput, mLevel + 1);
             return;
         }
-        auto glFunctorDrawable = getGLFunctorDrawable(drawable);
+        auto glFunctorDrawable = getFunctorDrawable(drawable);
         if (nullptr != glFunctorDrawable) {
             mOutput << std::string(mLevel * 2, ' ') << "drawGLFunctorDrawable" << std::endl;
             return;
@@ -157,10 +157,10 @@
         return nullptr;
     }
 
-    GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) {
+    FunctorDrawable* getFunctorDrawable(SkDrawable* drawable) {
         for (auto& child : mDisplayList.mChildFunctors) {
-            if (drawable == &child) {
-                return &child;
+            if (drawable == child) {
+                return child;
             }
         }
         return nullptr;
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
new file mode 100644
index 0000000..162d137
--- /dev/null
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -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.
+ */
+
+#pragma once
+
+#include "GlFunctorLifecycleListener.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class FunctorDrawable : public SkDrawable {
+public:
+    FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+    virtual ~FunctorDrawable() {}
+
+    virtual void syncFunctor() const = 0;
+
+protected:
+    virtual SkRect onGetBounds() override { return mBounds; }
+
+    Functor* mFunctor;
+    sp<GlFunctorLifecycleListener> mListener;
+    const SkRect mBounds;
+};
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index b0fec7a..90d5e71 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -18,7 +18,6 @@
 #include <GrContext.h>
 #include <private/hwui/DrawGlInfo.h>
 #include "GlFunctorLifecycleListener.h"
-#include "Properties.h"
 #include "RenderNode.h"
 #include "SkAndroidFrameworkUtils.h"
 #include "SkClipStack.h"
@@ -80,11 +79,6 @@
         return;
     }
 
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        canvas->clear(SK_ColorRED);
-        return;
-    }
-
     GLuint fboID = 0;
     SkISize fboSize;
     if (!GetFboDetails(canvas, &fboID, &fboSize)) {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index d9e65c9..dd6ef25 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -16,12 +16,8 @@
 
 #pragma once
 
-#include "GlFunctorLifecycleListener.h"
+#include "FunctorDrawable.h"
 
-#include <SkCanvas.h>
-#include <SkDrawable.h>
-
-#include <utils/Functor.h>
 #include <utils/RefBase.h>
 
 namespace android {
@@ -33,22 +29,16 @@
  * This drawable wraps a OpenGL functor enabling it to be recorded into a list
  * of Skia drawing commands.
  */
-class GLFunctorDrawable : public SkDrawable {
+class GLFunctorDrawable : public FunctorDrawable {
 public:
     GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
-            : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+            : FunctorDrawable(functor, listener, canvas) {}
     virtual ~GLFunctorDrawable();
 
-    void syncFunctor() const;
+    void syncFunctor() const override;
 
 protected:
-    virtual SkRect onGetBounds() override { return mBounds; }
     virtual void onDraw(SkCanvas* canvas) override;
-
-private:
-    Functor* mFunctor;
-    sp<GlFunctorLifecycleListener> mListener;
-    const SkRect mBounds;
 };
 
 };  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 82179a3..78b64b2 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -29,7 +29,7 @@
 
 void SkiaDisplayList::syncContents() {
     for (auto& functor : mChildFunctors) {
-        functor.syncFunctor();
+        functor->syncFunctor();
     }
     for (auto& animatedImage : mAnimatedImages) {
         animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 4f30f98..4c78539 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include "hwui/AnimatedImageDrawable.h"
-#include "GLFunctorDrawable.h"
+#include "FunctorDrawable.h"
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
@@ -77,7 +77,7 @@
      * that creates them. Allocator dtor invokes all SkDrawable dtors.
      */
     template <class T, typename... Params>
-    SkDrawable* allocateDrawable(Params&&... params) {
+    T* allocateDrawable(Params&&... params) {
         return allocator.create<T>(std::forward<Params>(params)...);
     }
 
@@ -155,7 +155,7 @@
      * cannot relocate.
      */
     std::deque<RenderNodeDrawable> mChildNodes;
-    std::deque<GLFunctorDrawable> mChildFunctors;
+    std::deque<FunctorDrawable*> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
     std::vector<VectorDrawableRoot*> mVectorDrawables;
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 83d7e6a..3c281e7 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -23,6 +23,8 @@
 #include "NinePatchUtils.h"
 #include "RenderNode.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
+#include "pipeline/skia/VkFunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -118,9 +120,16 @@
 
 void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
                                              uirenderer::GlFunctorLifecycleListener* listener) {
-    // Drawable dtor will be invoked when mChildFunctors deque is cleared.
-    mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
-    drawDrawable(&mDisplayList->mChildFunctors.back());
+    FunctorDrawable* functorDrawable;
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        functorDrawable = mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, listener,
+                asSkCanvas());
+    } else {
+        functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
+                asSkCanvas());
+    }
+    mDisplayList->mChildFunctors.push_back(functorDrawable);
+    drawDrawable(functorDrawable);
 }
 
 class VectorDrawable : public SkDrawable {
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 5cbe33d..e34f160 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -22,6 +22,7 @@
 #include "SkiaProfileRenderer.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/Frame.h"
+#include "VkFunctorDrawable.h"
 
 #include <SkSurface.h>
 #include <SkTypes.h>
@@ -146,9 +147,7 @@
 }
 
 void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
-    // TODO: we currently don't support OpenGL WebView's
-    DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
-    (*functor)(mode, nullptr);
+    VkFunctorDrawable::vkInvokeFunctor(functor);
 }
 
 sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
new file mode 100644
index 0000000..6486ddb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 "VkFunctorDrawable.h"
+#include <private/hwui/DrawGlInfo.h>
+
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <utils/GLUtils.h>
+
+namespace android {
+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.
+class ScopedDrawRequest {
+public:
+    ScopedDrawRequest() { beginDraw(); }
+private:
+    void beginDraw() {
+        std::lock_guard{sLock};
+
+        if (!sGLDrawThread) {
+            sGLDrawThread = new ThreadBase{};
+        }
+
+        if (!sGLDrawThread->isRunning()) {
+            sGLDrawThread->start("GLFunctorThread");
+        }
+
+        if (!sEglManager.hasEglContext()) {
+            sGLDrawThread->queue().runSync([]() {
+                sEglManager.initialize();
+            });
+        }
+    }
+};
+
+void VkFunctorDrawable::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);
+    });
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+void VkFunctorDrawable::onDraw(SkCanvas* canvas) {
+    ATRACE_CALL();
+
+    if (canvas->getGrContext() == nullptr) {
+        SkDEBUGF(("Attempting to draw VkFunctor into an unsupported surface"));
+        return;
+    }
+
+    ScopedDrawRequest _drawRequest{};
+
+    SkImageInfo surfaceInfo = canvas->imageInfo();
+
+    if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
+        // Buffer will be used as an OpenGL ES render target.
+        mFrameBuffer = new GraphicBuffer(
+                 //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+                 static_cast<uint32_t>(surfaceInfo.width()),
+                 static_cast<uint32_t>(surfaceInfo.height()),
+                 ColorTypeToPixelFormat(surfaceInfo.colorType()),
+                 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+                         GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+                 std::string("VkFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + "]");
+        status_t error = mFrameBuffer->initCheck();
+        if (error < 0) {
+            ALOGW("VkFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
+            return;
+        }
+
+        mFBInfo = surfaceInfo;
+    }
+
+    //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+    //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",
+                uirenderer::renderthread::EglManager::eglErrorString());
+        // We use an EGLImage to access the content of the GraphicBuffer
+        // The EGL image is later bound to a 2D texture
+        EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
+        AutoEglImage autoImage(display, clientBuffer);
+        if (autoImage.image == EGL_NO_IMAGE_KHR) {
+            ALOGW("Could not create EGL image, err =%s",
+                  uirenderer::renderthread::EglManager::eglErrorString());
+            return false;
+        }
+
+        AutoSkiaGlTexture glTexture;
+        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+        GL_CHECKPOINT(MODERATE);
+
+        glBindTexture(GL_TEXTURE_2D, 0);
+
+        DrawGlInfo info;
+        SkMatrix44 mat4(canvas->getTotalMatrix());
+        SkIRect clipBounds = canvas->getDeviceClipBounds();
+
+        info.clipLeft = clipBounds.fLeft;
+        info.clipTop = clipBounds.fTop;
+        info.clipRight = clipBounds.fRight;
+        info.clipBottom = clipBounds.fBottom;
+        info.isLayer = true;
+        info.width = mFBInfo.width();
+        info.height = mFBInfo.height();
+        mat4.asColMajorf(&info.transform[0]);
+
+        glViewport(0, 0, info.width, info.height);
+
+        AutoGLFramebuffer glFb;
+        // Bind texture to the frame buffer.
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                             glTexture.mTexture, 0);
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+            ALOGE("Failed framebuffer check for created target buffer: %s",
+                    GLUtils::getGLFramebufferError());
+            return false;
+        }
+
+        glDisable(GL_STENCIL_TEST);
+        glDisable(GL_SCISSOR_TEST);
+        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+        EGLSyncKHR glDrawFinishedFence =
+                eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+        LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
+                "Could not create sync fence %#x", eglGetError());
+        glFlush();
+        // TODO: export EGLSyncKHR in file descr
+        // TODO: import file desc in Vulkan Semaphore
+        // TODO: instead block the GPU: probably by using external Vulkan semaphore.
+        // Block the CPU until the glFlush finish.
+        EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
+                FENCE_TIMEOUT);
+        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;
+    paint.setBlendMode(SkBlendMode::kSrcOver);
+    canvas->save();
+    // The size of the image matches the size of the canvas. We've used the matrix already, while
+    // drawing into the offscreen surface, so we need to reset it here.
+    canvas->resetMatrix();
+
+    auto functorImage = SkImage::MakeFromAHardwareBuffer(
+        reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
+        nullptr, kBottomLeft_GrSurfaceOrigin);
+    canvas->drawImage(functorImage, 0, 0, &paint);
+    canvas->restore();
+}
+
+VkFunctorDrawable::~VkFunctorDrawable() {
+    if (mListener.get() != nullptr) {
+        ScopedDrawRequest _drawRequest{};
+        sGLDrawThread->queue().runSync([&]() {
+             mListener->onGlFunctorReleased(mFunctor);
+        });
+    }
+}
+
+void VkFunctorDrawable::syncFunctor() const {
+    ScopedDrawRequest _drawRequest{};
+    sGLDrawThread->queue().runSync([&]() {
+         (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+    });
+}
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
new file mode 100644
index 0000000..e37f6fb
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class VkFunctorDrawable : public FunctorDrawable {
+public:
+    VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+            : FunctorDrawable(functor, listener, canvas) {}
+    virtual ~VkFunctorDrawable();
+
+    void syncFunctor() const override;
+
+    static void vkInvokeFunctor(Functor* functor);
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+
+    // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
+    sp<GraphicBuffer> mFrameBuffer;
+    SkImageInfo mFBInfo;
+};
+
+};  // namespace skiapipeline
+};  // namespace uirenderer
+};  // namespace android
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 675df41..0cb23e5 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -23,7 +23,6 @@
 #include "utils/Color.h"
 #include "utils/StringUtils.h"
 
-#include "DeviceInfo.h"
 #include "Frame.h"
 #include "Properties.h"
 
@@ -127,7 +126,6 @@
     createContext();
     createPBufferSurface();
     makeCurrent(mPBufferSurface, nullptr, /* force */ true);
-    DeviceInfo::initialize();
 
     mSurfaceColorGamut = DataSpaceToColorGamut(
         static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace()));
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 7258a0a..a5dcc72 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -162,7 +162,7 @@
 }
 
 void RenderThread::initThreadLocals() {
-    mDisplayInfo = DeviceInfo::queryDisplayInfo();
+    mDisplayInfo = DeviceInfo::get()->displayInfo();
     nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
     mTimeLord.setFrameInterval(frameIntervalNanos);
     initializeDisplayEventReceiver();
@@ -246,6 +246,9 @@
         mGrContext->releaseResourcesAndAbandonContext();
     }
     mGrContext = std::move(context);
+    if (mGrContext) {
+        DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
+    }
 }
 
 int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 038e13c..4881172 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,7 +16,6 @@
 
 #include "VulkanManager.h"
 
-#include "DeviceInfo.h"
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
@@ -399,8 +398,6 @@
 
     free_features_extensions_structs(features);
 
-    DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0e6582c..c35f512 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <DeviceInfo.h>
 #include <DisplayList.h>
 #include <Matrix.h>
 #include <Properties.h>
@@ -179,12 +178,6 @@
     static sp<RenderNode> createNode(
             int left, int top, int right, int bottom,
             std::function<void(RenderProperties& props, Canvas& canvas)> setup) {
-#if HWUI_NULL_GPU
-        // if RenderNodes are being sync'd/used, device info will be needed, since
-        // DeviceInfo::maxTextureSize() affects layer property
-        DeviceInfo::initialize();
-#endif
-
         sp<RenderNode> node = new RenderNode();
         RenderProperties& props = node->mutateStagingProperties();
         props.setLeftTopRightBottom(left, top, right, bottom);
@@ -202,12 +195,6 @@
     static sp<RenderNode> createNode(
             int left, int top, int right, int bottom,
             std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) {
-#if HWUI_NULL_GPU
-        // if RenderNodes are being sync'd/used, device info will be needed, since
-        // DeviceInfo::maxTextureSize() affects layer property
-        DeviceInfo::initialize();
-#endif
-
         sp<RenderNode> node = new RenderNode();
         RenderProperties& props = node->mutateStagingProperties();
         props.setLeftTopRightBottom(left, top, right, bottom);
@@ -233,11 +220,6 @@
             std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)>
                     setup,
             const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
-#if HWUI_NULL_GPU
-        // if RenderNodes are being sync'd/used, device info will be needed, since
-        // DeviceInfo::maxTextureSize() affects layer property
-        DeviceInfo::initialize();
-#endif
         sp<RenderNode> node = new RenderNode();
         if (name) {
             node->setName(name);
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
index 85655fc..3e8e057 100644
--- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -22,8 +22,6 @@
 using namespace android::uirenderer;
 
 TEST(RenderProperties, layerValidity) {
-    DeviceInfo::initialize();
-
     const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
     ASSERT_LE(2048, maxTextureSize);
     ASSERT_GT(100000, maxTextureSize);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 6c398ee..415f9e8 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,6 +20,7 @@
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
@@ -46,7 +47,8 @@
     SkCanvas dummyCanvas;
     RenderNodeDrawable drawable(nullptr, &dummyCanvas);
     skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
-    skiaDL->mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
+    GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
+    skiaDL->mChildFunctors.push_back(&functorDrawable);
     skiaDL->mMutableImages.push_back(nullptr);
     skiaDL->mVectorDrawables.push_back(nullptr);
     skiaDL->mProjectionReceiver = &drawable;
@@ -95,7 +97,8 @@
 
     SkCanvas dummyCanvas;
     TestUtils::MockFunctor functor;
-    skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+    GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
+    skiaDL.mChildFunctors.push_back(&functorDrawable);
 
     SkRect bounds = SkRect::MakeWH(200, 200);
     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index d35fe4f..9f71e91 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -72,6 +72,26 @@
     }
 }
 
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
+    switch (colorType) {
+        case kRGBA_8888_SkColorType:
+            return PIXEL_FORMAT_RGBA_8888;
+        case kRGBA_F16_SkColorType:
+            return PIXEL_FORMAT_RGBA_FP16;
+        case kRGB_565_SkColorType:
+            return PIXEL_FORMAT_RGB_565;
+        case kRGB_888x_SkColorType:
+            return PIXEL_FORMAT_RGBX_8888;
+        case kRGBA_1010102_SkColorType:
+            return PIXEL_FORMAT_RGBA_1010102;
+        case kARGB_4444_SkColorType:
+            return PIXEL_FORMAT_RGBA_4444;
+        default:
+            ALOGW("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
+            return PIXEL_FORMAT_RGBA_8888;
+    }
+}
+
 SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) {
     switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) {
         case HAL_DATASPACE_STANDARD_BT709:
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index ff0e755..e935a0d 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -18,6 +18,7 @@
 
 #include <math.h>
 #include <system/graphics.h>
+#include <ui/PixelFormat.h>
 
 #include <SkColor.h>
 #include <SkColorSpace.h>
@@ -116,6 +117,8 @@
 
 SkColorType PixelFormatToColorType(android_pixel_format pixelFormat);
 
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
+
 SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace);
 
 sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index bf27300..fcd036c 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -59,5 +59,22 @@
 #endif
 }
 
+const char* GLUtils::getGLFramebufferError() {
+    switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+        case GL_FRAMEBUFFER_COMPLETE:
+            return "GL_FRAMEBUFFER_COMPLETE";
+        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+            return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+            return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+        case GL_FRAMEBUFFER_UNSUPPORTED:
+            return "GL_FRAMEBUFFER_UNSUPPORTED";
+        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+            return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+        default:
+            return "Unknown error";
+    }
+}
+
 };  // namespace uirenderer
 };  // namespace android
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index debfb5d..ca8810b 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -20,6 +20,12 @@
 
 #include <log/log.h>
 
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -43,8 +49,53 @@
      */
     static bool dumpGLErrors();
 
+    static const char* getGLFramebufferError();
 };  // class GLUtils
 
+class AutoEglImage {
+public:
+    AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
+        EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
+        image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
+                                  imageAttrs);
+    }
+
+    ~AutoEglImage() {
+        if (image != EGL_NO_IMAGE_KHR) {
+            eglDestroyImageKHR(mDisplay, image);
+        }
+    }
+
+    EGLImageKHR image = EGL_NO_IMAGE_KHR;
+
+private:
+    EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+class AutoSkiaGlTexture {
+public:
+    AutoSkiaGlTexture() {
+        glGenTextures(1, &mTexture);
+        glBindTexture(GL_TEXTURE_2D, mTexture);
+    }
+
+    ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
+
+    GLuint mTexture = 0;
+};
+
+class AutoGLFramebuffer {
+public:
+    AutoGLFramebuffer() {
+        glGenFramebuffers(1, &mFb);
+        glBindFramebuffer(GL_FRAMEBUFFER, mFb);
+    }
+
+    ~AutoGLFramebuffer() { glDeleteFramebuffers(1, &mFb); }
+
+    GLuint mFb;
+};
+
 } /* namespace uirenderer */
 } /* namespace android */
 
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 255619c..52cb75e 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -25,6 +25,58 @@
 namespace android {
 namespace os {
 
+/**
+ * A wrapper for a union type to contain multiple types of values.
+ *
+ */
+struct StatsLogValue {
+  // Keep in sync with FieldValue.h
+  enum STATS_LOG_VALUE_TYPE {
+    UNKNOWN = 0,
+    INT = 1,
+    LONG = 2,
+    FLOAT = 3,
+    DOUBLE = 4,
+    STRING = 5,
+    STORAGE = 6
+  };
+
+  StatsLogValue() : type(UNKNOWN) {}
+
+  StatsLogValue(int32_t v) {
+    int_value = v;
+    type = INT;
+  }
+
+  StatsLogValue(int64_t v) {
+    long_value = v;
+    type = LONG;
+  }
+
+  StatsLogValue(float v) {
+    float_value = v;
+    type = FLOAT;
+  }
+
+  StatsLogValue(const std::string& v) {
+    str_value = v;
+    type = STRING;
+  }
+
+  void setType(STATS_LOG_VALUE_TYPE t) { type = t; }
+
+  union {
+    int32_t int_value;
+    int64_t long_value;
+    float float_value;
+    double double_value;
+  };
+  std::string str_value;
+  std::vector<uint8_t> storage_value;
+
+  STATS_LOG_VALUE_TYPE type;
+};
+
 // Represents a parcelable object. Only used to send data from Android OS to statsd.
 class StatsLogEventWrapper : public android::Parcelable {
  public:
@@ -36,8 +88,22 @@
 
   android::status_t readFromParcel(const android::Parcel* in);
 
-  // These are public for ease of conversion.
-  std::vector<uint8_t> bytes;
+  int getTagId() const { return mTagId; }
+
+  int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; }
+
+  int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
+
+  std::vector<StatsLogValue> getElements() const { return mElements; }
+
+ private:
+  int mTagId;
+
+  int64_t mElapsedRealTimeNs;
+
+  int64_t mWallClockTimeNs;
+
+  std::vector<StatsLogValue> mElements;
 };
 } // Namespace os
 } // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 8b3aa9a..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -32,13 +32,70 @@
 StatsLogEventWrapper::StatsLogEventWrapper(){};
 
 status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
-    out->writeByteVector(bytes);
-    return ::android::NO_ERROR;
+  // Implement me if desired. We don't currently use this.
+  ALOGE(
+      "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not "
+      "implemented.");
+  (void)out;  // To prevent compile error of unused parameter 'in'
+  return UNKNOWN_ERROR;
 };
 
 status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
-    in->readByteVector(&bytes);
-    return ::android::NO_ERROR;
+  status_t res = OK;
+  if (in == NULL) {
+    ALOGE("statsd received parcel argument was NULL.");
+    return BAD_VALUE;
+  }
+  if ((res = in->readInt32(&mTagId)) != OK) {
+    ALOGE("statsd could not read tagId from parcel");
+    return res;
+  }
+  if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) {
+    ALOGE("statsd could not read elapsed real time from parcel");
+    return res;
+  }
+  if ((res = in->readInt64(&mWallClockTimeNs)) != OK) {
+    ALOGE("statsd could not read wall clock time from parcel");
+    return res;
+  }
+  int dataSize = 0;
+  if ((res = in->readInt32(&dataSize)) != OK) {
+    ALOGE("statsd could not read data size from parcel");
+    return res;
+  }
+  if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 ||
+      dataSize <= 0) {
+    ALOGE("statsd received invalid parcel");
+    return BAD_VALUE;
+  }
+
+  for (int i = 0; i < dataSize; i++) {
+    int type = in->readInt32();
+    switch (type) {
+      case StatsLogValue::INT:
+        mElements.push_back(StatsLogValue(in->readInt32()));
+        break;
+      case StatsLogValue::LONG:
+        mElements.push_back(StatsLogValue(in->readInt64()));
+        break;
+      case StatsLogValue::STRING:
+        mElements.push_back(
+            StatsLogValue(std::string(String8(in->readString16()).string())));
+        break;
+      case StatsLogValue::FLOAT:
+        mElements.push_back(StatsLogValue(in->readFloat()));
+        break;
+      case StatsLogValue::STORAGE:
+        mElements.push_back(StatsLogValue());
+        mElements.back().setType(StatsLogValue::STORAGE);
+        in->readByteVector(&(mElements.back().storage_value));
+        break;
+      default:
+        ALOGE("unrecognized data type: %d", type);
+        return BAD_TYPE;
+    }
+  }
+  return NO_ERROR;
 };
 
 } // Namespace os
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 89827bc..7492aa6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1108,12 +1108,9 @@
 
     /**
      * Sets playback rate using {@link PlaybackParams}. The object sets its internal
-     * PlaybackParams to the input, except that the object remembers previous speed
-     * when input speed is zero. This allows the object to resume at previous speed
-     * when play() is called. Calling it before the object is prepared does not change
-     * the object state. After the object is prepared, calling it with zero speed is
-     * equivalent to calling pause(). After the object is prepared, calling it with
-     * non-zero speed is equivalent to calling play().
+     * PlaybackParams to the input. This allows the object to resume at previous speed
+     * when play() is called. Speed of zero is not allowed. Calling it does not change
+     * the object state.
      *
      * @param params the playback params.
      */
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 6ae4d40..6187900 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -544,29 +544,11 @@
     private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
 
     /**
-     * Create a request parcel which can be routed to the native media
-     * player using {@link #invoke(Parcel, Parcel)}. The Parcel
-     * returned has the proper InterfaceToken set. The caller should
-     * not overwrite that token, i.e it can only append data to the
-     * Parcel.
-     *
-     * @return A parcel suitable to hold a request for the native
-     * player.
-     * {@hide}
-     */
-    @Override
-    public Parcel newRequest() {
-        Parcel parcel = Parcel.obtain();
-        return parcel;
-    }
-
-    /**
      * Invoke a generic method on the native player using opaque protocol
      * buffer message for the request and reply. Both payloads' format is a
      * convention between the java caller and the native player.
      *
-     * @param request PlayerMessage for the extension. The
-     * caller must use {@link #newRequest()} to get one.
+     * @param msg PlayerMessage for the extension.
      *
      * @return PlayerMessage with the data returned by the
      * native player.
@@ -1536,91 +1518,6 @@
     }
 
     /**
-     * Gets the media metadata.
-     *
-     * @param update_only controls whether the full set of available
-     * metadata is returned or just the set that changed since the
-     * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
-     * #METADATA_ALL}.
-     *
-     * @param apply_filter if true only metadata that matches the
-     * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
-     * #BYPASS_METADATA_FILTER}.
-     *
-     * @return The metadata, possibly empty. null if an error occured.
-     // FIXME: unhide.
-     * {@hide}
-     */
-    @Override
-    public Metadata getMetadata(final boolean update_only,
-                                final boolean apply_filter) {
-        Parcel reply = Parcel.obtain();
-        Metadata data = new Metadata();
-
-        if (!native_getMetadata(update_only, apply_filter, reply)) {
-            reply.recycle();
-            return null;
-        }
-
-        // Metadata takes over the parcel, don't recycle it unless
-        // there is an error.
-        if (!data.parse(reply)) {
-            reply.recycle();
-            return null;
-        }
-        return data;
-    }
-
-    /**
-     * Set a filter for the metadata update notification and update
-     * retrieval. The caller provides 2 set of metadata keys, allowed
-     * and blocked. The blocked set always takes precedence over the
-     * allowed one.
-     * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
-     * shorthands to allow/block all or no metadata.
-     *
-     * By default, there is no filter set.
-     *
-     * @param allow Is the set of metadata the client is interested
-     *              in receiving new notifications for.
-     * @param block Is the set of metadata the client is not interested
-     *              in receiving new notifications for.
-     * @return The call status code.
-     *
-     // FIXME: unhide.
-     * {@hide}
-     */
-    @Override
-    public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
-        // Do our serialization manually instead of calling
-        // Parcel.writeArray since the sets are made of the same type
-        // we avoid paying the price of calling writeValue (used by
-        // writeArray) which burns an extra int per element to encode
-        // the type.
-        Parcel request =  newRequest();
-
-        // The parcel starts already with an interface token. There
-        // are 2 filters. Each one starts with a 4bytes number to
-        // store the len followed by a number of int (4 bytes as well)
-        // representing the metadata type.
-        int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
-
-        if (request.dataCapacity() < capacity) {
-            request.setDataCapacity(capacity);
-        }
-
-        request.writeInt(allow.size());
-        for(Integer t: allow) {
-            request.writeInt(t);
-        }
-        request.writeInt(block.size());
-        for(Integer t: block) {
-            request.writeInt(t);
-        }
-        return native_setMetadataFilter(request);
-    }
-
-    /**
      * Resets the MediaPlayer2 to its uninitialized state. After calling
      * this method, you will have to initialize it again by setting the
      * data source and calling prepare().
@@ -1802,32 +1699,6 @@
 
     private native void _setAuxEffectSendLevel(float level);
 
-    /*
-     * @param update_only If true fetch only the set of metadata that have
-     *                    changed since the last invocation of getMetadata.
-     *                    The set is built using the unfiltered
-     *                    notifications the native player sent to the
-     *                    MediaPlayer2Manager during that period of
-     *                    time. If false, all the metadatas are considered.
-     * @param apply_filter  If true, once the metadata set has been built based on
-     *                     the value update_only, the current filter is applied.
-     * @param reply[out] On return contains the serialized
-     *                   metadata. Valid only if the call was successful.
-     * @return The status code.
-     */
-    private native final boolean native_getMetadata(boolean update_only,
-                                                    boolean apply_filter,
-                                                    Parcel reply);
-
-    /*
-     * @param request Parcel with the 2 serialized lists of allowed
-     *                metadata types followed by the one to be
-     *                dropped. Each list starts with an integer
-     *                indicating the number of metadata type elements.
-     * @return The status code.
-     */
-    private native final int native_setMetadataFilter(Parcel request);
-
     private static native final void native_init();
     private native final void native_setup(Object mediaplayer2_this);
     private native final void native_finalize();
@@ -1903,25 +1774,6 @@
             mFormat = format;
         }
 
-        /**
-         * Flatten this object in to a Parcel.
-         *
-         * @param dest The Parcel in which the object should be written.
-         * @param flags Additional flags about how the object should be written.
-         * May be 0 or {@link android.os.Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
-         */
-        /* package private */ void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mTrackType);
-            dest.writeString(getLanguage());
-
-            if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
-                dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
-                dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
-                dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
-                dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
-            }
-        }
-
         @Override
         public String toString() {
             StringBuilder out = new StringBuilder(128);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7681cc3..3870124 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,8 +88,6 @@
     name: "libmedia2_jni",
 
     srcs: [
-        "android_media_Media2HTTPConnection.cpp",
-        "android_media_Media2HTTPService.cpp",
         "android_media_Media2DataSource.cpp",
         "android_media_MediaMetricsJNI.cpp",
         "android_media_MediaPlayer2.cpp",
@@ -142,6 +140,7 @@
         "libstagefright_player2",
         "libstagefright_rtsp",
         "libstagefright_timedtext2",
+        "libmedia2_jni_core",
     ],
 
     group_static_libs: true,
diff --git a/media/jni/android_media_Media2HTTPConnection.cpp b/media/jni/android_media_Media2HTTPConnection.cpp
deleted file mode 100644
index d02ee06..0000000
--- a/media/jni/android_media_Media2HTTPConnection.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Media2HTTPConnection-JNI"
-#include <utils/Log.h>
-
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-#include "android_media_Media2HTTPConnection.h"
-#include "android_util_Binder.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-namespace android {
-
-static const size_t kBufferSize = 32768;
-
-JMedia2HTTPConnection::JMedia2HTTPConnection(JNIEnv *env, jobject thiz) {
-    mMedia2HTTPConnectionObj = env->NewGlobalRef(thiz);
-    CHECK(mMedia2HTTPConnectionObj != NULL);
-
-    ScopedLocalRef<jclass> media2HTTPConnectionClass(
-            env, env->GetObjectClass(mMedia2HTTPConnectionObj));
-    CHECK(media2HTTPConnectionClass.get() != NULL);
-
-    mConnectMethod = env->GetMethodID(
-            media2HTTPConnectionClass.get(),
-            "connect",
-            "(Ljava/lang/String;Ljava/lang/String;)Z");
-    CHECK(mConnectMethod != NULL);
-
-    mDisconnectMethod = env->GetMethodID(
-            media2HTTPConnectionClass.get(),
-            "disconnect",
-            "()V");
-    CHECK(mDisconnectMethod != NULL);
-
-    mReadAtMethod = env->GetMethodID(
-            media2HTTPConnectionClass.get(),
-            "readAt",
-            "(J[BI)I");
-    CHECK(mReadAtMethod != NULL);
-
-    mGetSizeMethod = env->GetMethodID(
-            media2HTTPConnectionClass.get(),
-            "getSize",
-            "()J");
-    CHECK(mGetSizeMethod != NULL);
-
-    mGetMIMETypeMethod = env->GetMethodID(
-            media2HTTPConnectionClass.get(),
-            "getMIMEType",
-            "()Ljava/lang/String;");
-    CHECK(mGetMIMETypeMethod != NULL);
-
-    mGetUriMethod = env->GetMethodID(
-            media2HTTPConnectionClass.get(),
-            "getUri",
-            "()Ljava/lang/String;");
-    CHECK(mGetUriMethod != NULL);
-
-    ScopedLocalRef<jbyteArray> tmp(
-        env, env->NewByteArray(kBufferSize));
-    mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
-    CHECK(mByteArrayObj != NULL);
-}
-
-JMedia2HTTPConnection::~JMedia2HTTPConnection() {
-    JNIEnv *env = JavaVMHelper::getJNIEnv();
-    env->DeleteGlobalRef(mMedia2HTTPConnectionObj);
-    env->DeleteGlobalRef(mByteArrayObj);
-}
-
-bool JMedia2HTTPConnection::connect(
-        const char *uri, const KeyedVector<String8, String8> *headers) {
-    String8 tmp("");
-    if (headers != NULL) {
-        for (size_t i = 0; i < headers->size(); ++i) {
-            tmp.append(headers->keyAt(i));
-            tmp.append(String8(": "));
-            tmp.append(headers->valueAt(i));
-            tmp.append(String8("\r\n"));
-        }
-    }
-
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-    jstring juri = env->NewStringUTF(uri);
-    jstring jheaders = env->NewStringUTF(tmp.string());
-
-    jboolean ret =
-        env->CallBooleanMethod(mMedia2HTTPConnectionObj, mConnectMethod, juri, jheaders);
-
-    env->DeleteLocalRef(juri);
-    env->DeleteLocalRef(jheaders);
-
-    return (bool)ret;
-}
-
-void JMedia2HTTPConnection::disconnect() {
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-    env->CallVoidMethod(mMedia2HTTPConnectionObj, mDisconnectMethod);
-}
-
-ssize_t JMedia2HTTPConnection::readAt(off64_t offset, void *data, size_t size) {
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-
-    if (size > kBufferSize) {
-        size = kBufferSize;
-    }
-
-    jint n = env->CallIntMethod(
-            mMedia2HTTPConnectionObj, mReadAtMethod, (jlong)offset, mByteArrayObj, (jint)size);
-
-    if (n > 0) {
-        env->GetByteArrayRegion(
-                mByteArrayObj,
-                0,
-                n,
-                (jbyte *)data);
-    }
-
-    return n;
-}
-
-off64_t JMedia2HTTPConnection::getSize() {
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-    return (off64_t)(env->CallLongMethod(mMedia2HTTPConnectionObj, mGetSizeMethod));
-}
-
-status_t JMedia2HTTPConnection::getMIMEType(String8 *mimeType) {
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-    jstring jmime = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetMIMETypeMethod);
-    jboolean flag = env->ExceptionCheck();
-    if (flag) {
-        env->ExceptionClear();
-        return UNKNOWN_ERROR;
-    }
-
-    const char *str = env->GetStringUTFChars(jmime, 0);
-    if (str != NULL) {
-        *mimeType = String8(str);
-    } else {
-        *mimeType = "application/octet-stream";
-    }
-    env->ReleaseStringUTFChars(jmime, str);
-    return OK;
-}
-
-status_t JMedia2HTTPConnection::getUri(String8 *uri) {
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-    jstring juri = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetUriMethod);
-    jboolean flag = env->ExceptionCheck();
-    if (flag) {
-        env->ExceptionClear();
-        return UNKNOWN_ERROR;
-    }
-
-    const char *str = env->GetStringUTFChars(juri, 0);
-    *uri = String8(str);
-    env->ReleaseStringUTFChars(juri, str);
-    return OK;
-}
-
-}  // namespace android
diff --git a/media/jni/android_media_Media2HTTPConnection.h b/media/jni/android_media_Media2HTTPConnection.h
deleted file mode 100644
index 14bc677..0000000
--- a/media/jni/android_media_Media2HTTPConnection.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
-#define _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
-
-#include "jni.h"
-
-#include <media/MediaHTTPConnection.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-struct JMedia2HTTPConnection : public MediaHTTPConnection {
-    JMedia2HTTPConnection(JNIEnv *env, jobject thiz);
-
-    virtual bool connect(
-            const char *uri, const KeyedVector<String8, String8> *headers) override;
-
-    virtual void disconnect() override;
-    virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
-    virtual off64_t getSize() override;
-    virtual status_t getMIMEType(String8 *mimeType) override;
-    virtual status_t getUri(String8 *uri) override;
-
-protected:
-    virtual ~JMedia2HTTPConnection();
-
-private:
-    jobject mMedia2HTTPConnectionObj;
-    jmethodID mConnectMethod;
-    jmethodID mDisconnectMethod;
-    jmethodID mReadAtMethod;
-    jmethodID mGetSizeMethod;
-    jmethodID mGetMIMETypeMethod;
-    jmethodID mGetUriMethod;
-
-    jbyteArray mByteArrayObj;
-
-    DISALLOW_EVIL_CONSTRUCTORS(JMedia2HTTPConnection);
-};
-
-}  // namespace android
-
-#endif  // _ANDROID_MEDIA_MEDIA2HTTPCONNECTION_H_
diff --git a/media/jni/android_media_Media2HTTPService.cpp b/media/jni/android_media_Media2HTTPService.cpp
deleted file mode 100644
index 1c63889..0000000
--- a/media/jni/android_media_Media2HTTPService.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "Media2HTTPService-JNI"
-#include <utils/Log.h>
-
-#include "android_media_Media2HTTPConnection.h"
-#include "android_media_Media2HTTPService.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-namespace android {
-
-JMedia2HTTPService::JMedia2HTTPService(JNIEnv *env, jobject thiz) {
-    mMedia2HTTPServiceObj = env->NewGlobalRef(thiz);
-    CHECK(mMedia2HTTPServiceObj != NULL);
-
-    ScopedLocalRef<jclass> media2HTTPServiceClass(env, env->GetObjectClass(mMedia2HTTPServiceObj));
-    CHECK(media2HTTPServiceClass.get() != NULL);
-
-    mMakeHTTPConnectionMethod = env->GetMethodID(
-            media2HTTPServiceClass.get(),
-            "makeHTTPConnection",
-            "()Landroid/media/Media2HTTPConnection;");
-    CHECK(mMakeHTTPConnectionMethod != NULL);
-}
-
-JMedia2HTTPService::~JMedia2HTTPService() {
-    JNIEnv *env = JavaVMHelper::getJNIEnv();
-    env->DeleteGlobalRef(mMedia2HTTPServiceObj);
-}
-
-sp<MediaHTTPConnection> JMedia2HTTPService::makeHTTPConnection() {
-    JNIEnv* env = JavaVMHelper::getJNIEnv();
-    jobject media2HTTPConnectionObj =
-        env->CallObjectMethod(mMedia2HTTPServiceObj, mMakeHTTPConnectionMethod);
-
-    return new JMedia2HTTPConnection(env, media2HTTPConnectionObj);
-}
-
-}  // namespace android
diff --git a/media/jni/android_media_Media2HTTPService.h b/media/jni/android_media_Media2HTTPService.h
deleted file mode 100644
index 30d03f5..0000000
--- a/media/jni/android_media_Media2HTTPService.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
-#define _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
-
-#include "jni.h"
-
-#include <media/MediaHTTPService.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-struct JMedia2HTTPService : public MediaHTTPService {
-    JMedia2HTTPService(JNIEnv *env, jobject thiz);
-
-    virtual sp<MediaHTTPConnection> makeHTTPConnection() override;
-
-protected:
-    virtual ~JMedia2HTTPService();
-
-private:
-    jobject mMedia2HTTPServiceObj;
-
-    jmethodID mMakeHTTPConnectionMethod;
-
-    DISALLOW_EVIL_CONSTRUCTORS(JMedia2HTTPService);
-};
-
-}  // namespace android
-
-#endif  // _ANDROID_MEDIA_MEDIA2HTTPSERVICE_H_
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index d4c84b5..1a844cc 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/foundation/ByteUtils.h>  // for FOURCC definition
 #include <mediaplayer2/JAudioTrack.h>
 #include <mediaplayer2/JavaVMHelper.h>
+#include <mediaplayer2/JMedia2HTTPService.h>
 #include <mediaplayer2/mediaplayer2.h>
 #include <stdio.h>
 #include <assert.h>
@@ -45,7 +46,6 @@
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
 #include "android_media_BufferingParams.h"
-#include "android_media_Media2HTTPService.h"
 #include "android_media_Media2DataSource.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 212f398..0ccf13e 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -107,6 +107,7 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="16dip"
         android:elevation="@dimen/preview_controls_elevation"
+        android:tint="?android:attr/textColorPrimaryInverse"
         android:background="@drawable/print_button">
     </ImageButton>
 
diff --git a/packages/SettingsLib/HelpUtils/res/values/strings.xml b/packages/SettingsLib/HelpUtils/res/values/strings.xml
index ae07f5d..3e882bc 100644
--- a/packages/SettingsLib/HelpUtils/res/values/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <!-- Label for Help and feedback menu item -->
+    <!-- Label for Help and feedback menu item [CHAR LIMIT=45]-->
     <string name="help_feedback_label">Help &amp; feedback</string>
 
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index d4c9794..b2f0882 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -2,6 +2,7 @@
     name: "SettingsLibRestrictedLockUtils",
 
     srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
 
     libs: [
         "androidx.annotation_annotation",
diff --git a/packages/SettingsLib/res/layout/restricted_icon.xml b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
similarity index 76%
rename from packages/SettingsLib/res/layout/restricted_icon.xml
rename to packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
index 724a524..0f02abd 100644
--- a/packages/SettingsLib/res/layout/restricted_icon.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
@@ -15,6 +15,7 @@
 -->
 <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/restricted_icon"
-    android:layout_width="@dimen/restricted_icon_size"
-    android:layout_height="@dimen/restricted_icon_size"
-    android:src="@drawable/ic_info" />
\ No newline at end of file
+    android:layout_width="@*android:dimen/config_restricted_icon_size"
+    android:layout_height="@*android:dimen/config_restricted_icon_size"
+    android:tint="?android:attr/colorAccent"
+    android:src="@*android:drawable/ic_info" />
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
new file mode 100644
index 0000000..7e4460b
--- /dev/null
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+    <string name="enabled_by_admin">Enabled by admin</string>
+    <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
+    <string name="disabled_by_admin">Disabled by admin</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
index b286df0..e1f6cdf 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_widget.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
@@ -16,9 +16,10 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/restricted_icon"
-        android:layout_width="@dimen/restricted_icon_size"
-        android:layout_height="@dimen/restricted_icon_size"
-        android:src="@drawable/ic_info"
+        android:layout_width="@*android:dimen/config_restricted_icon_size"
+        android:layout_height="@*android:dimen/config_restricted_icon_size"
+        android:tint="?android:attr/colorAccent"
+        android:src="@*android:drawable/ic_info"
         android:gravity="end|center_vertical" />
     <!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml -->
     <Switch xmlns:android="http://schemas.android.com/apk/res/android"
@@ -28,4 +29,4 @@
         android:focusable="false"
         android:clickable="false"
         android:background="@null" />
-</merge>
\ No newline at end of file
+</merge>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 8094b02..a9c5061 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -36,7 +36,6 @@
     <dimen name="two_target_pref_medium_icon_size">32dp</dimen>
 
     <!-- Lock icon for preferences locked by admin -->
-    <dimen name="restricted_icon_size">16dp</dimen>
     <dimen name="restricted_icon_padding">4dp</dimen>
 
     <dimen name="wifi_preference_badge_padding">8dip</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ea6844e..332ced6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -965,11 +965,6 @@
     <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
     <string name="disabled_by_admin_summary_text">Controlled by admin</string>
 
-    <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
-    <string name="enabled_by_admin">Enabled by admin</string>
-    <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
-    <string name="disabled_by_admin">Disabled by admin</string>
-
     <!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
     <string name="disabled">Disabled</string>
     <!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 0094c2c..c03ba9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -29,6 +29,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -55,9 +56,15 @@
      * @return drawables for displaying with settings that are locked by a device admin.
      */
     public static Drawable getRestrictedPadlock(Context context) {
-        Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info);
+        Drawable restrictedPadlock = context.getDrawable(android.R.drawable.ic_info);
         final int iconSize = context.getResources().getDimensionPixelSize(
-                R.dimen.restricted_icon_size);
+                android.R.dimen.config_restricted_icon_size);
+
+        TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+        int colorAccent = ta.getColor(0, 0);
+        ta.recycle();
+        restrictedPadlock.setTint(colorAccent);
+
         restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
         return restrictedPadlock;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index e8f47e1..5a64e02 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -211,6 +211,26 @@
         }
     }
 
+    /**
+     * Attempts to get the name of a remote device, otherwise returns the address.
+     *
+     * @param device The remote device.
+     * @return The name, or if unavailable, the address.
+     */
+    public String getName(BluetoothDevice device) {
+        CachedBluetoothDevice cachedDevice = findDevice(device);
+        if (cachedDevice != null && cachedDevice.getName() != null) {
+            return cachedDevice.getName();
+        }
+
+        String name = device.getAliasName();
+        if (name != null) {
+            return name;
+        }
+
+        return device.getAddress();
+    }
+
     public synchronized void clearNonBondedDevices() {
 
         mCachedDevicesMapForHearingAids.entrySet().removeIf(entries
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 8c4bff5..c8d4fc8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -29,7 +29,7 @@
 import java.util.List;
 
 /**
- * HidProfile handles Bluetooth HID profile.
+ * HidDeviceProfile handles Bluetooth HID Device role
  */
 public class HidDeviceProfile implements LocalBluetoothProfile {
     private static final String TAG = "HidDeviceProfile";
@@ -37,7 +37,6 @@
     private static final int ORDINAL = 18;
     // HID Device Profile is always preferred.
     private static final int PREFERRED_VALUE = -1;
-    private static final boolean DEBUG = true;
 
     private final CachedBluetoothDeviceManager mDeviceManager;
     private final LocalBluetoothProfileManager mProfileManager;
@@ -59,9 +58,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (DEBUG) {
-                Log.d(TAG,"Bluetooth service connected :-)");
-            }
+            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();
@@ -81,9 +78,7 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (DEBUG) {
-                Log.d(TAG, "Bluetooth service disconnected");
-            }
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
@@ -110,6 +105,7 @@
 
     @Override
     public boolean connect(BluetoothDevice device) {
+        // Don't invoke method in service because settings is not allowed to connect this profile.
         return false;
     }
 
@@ -126,11 +122,7 @@
         if (mService == null) {
             return BluetoothProfile.STATE_DISCONNECTED;
         }
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
-        return !deviceList.isEmpty() && deviceList.contains(device)
-                ? mService.getConnectionState(device)
-                : BluetoothProfile.STATE_DISCONNECTED;
+        return mService.getConnectionState(device);
     }
 
     @Override
@@ -185,9 +177,7 @@
     }
 
     protected void finalize() {
-        if (DEBUG) {
-            Log.d(TAG, "finalize()");
-        }
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7d334eb..7ad2e28c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -32,11 +32,10 @@
 import java.util.List;
 
 /**
- * MapClientProfile handles Bluetooth MAP profile.
+ * MapClientProfile handles the Bluetooth MAP MCE role.
  */
 public final class MapClientProfile implements LocalBluetoothProfile {
     private static final String TAG = "MapClientProfile";
-    private static boolean V = false;
 
     private BluetoothMapClient mService;
     private boolean mIsProfileReady;
@@ -60,7 +59,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
+            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();
@@ -82,14 +81,14 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
     }
 
     public boolean isProfileReady() {
-        if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+        Log.d(TAG, "isProfileReady(): "+ mIsProfileReady);
         return mIsProfileReady;
     }
 
@@ -115,18 +114,16 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> connectedDevices = getConnectedDevices();
-        if (connectedDevices != null && connectedDevices.contains(device)) {
-            // Connect to same device, Ignore it
-            Log.d(TAG,"Ignoring Connect");
-            return true;
+        if (mService == null) {
+            return false;
         }
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         // Downgrade priority as user is disconnecting.
         if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
             mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -135,23 +132,30 @@
     }
 
     public int getConnectionStatus(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
         return mService.getConnectionState(device);
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return BluetoothProfile.PRIORITY_OFF;
+        }
         return mService.getPriority(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -162,7 +166,9 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
-        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
         return mService.getDevicesMatchingConnectionStates(
               new int[] {BluetoothProfile.STATE_CONNECTED,
                          BluetoothProfile.STATE_CONNECTING,
@@ -200,11 +206,11 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
-                                                                       mService);
+                        mService);
                 mService = null;
             }catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up MAP Client proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index b295f24..8fefb2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -34,7 +34,6 @@
 
 public final class PbapClientProfile implements LocalBluetoothProfile {
     private static final String TAG = "PbapClientProfile";
-    private static boolean V = false;
 
     private BluetoothPbapClient mService;
     private boolean mIsProfileReady;
@@ -56,9 +55,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) {
-                Log.d(TAG,"Bluetooth service connected");
-            }
+            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();
@@ -77,9 +74,7 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) {
-                Log.d(TAG,"Bluetooth service disconnected");
-            }
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
@@ -131,31 +126,16 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (V) {
-            Log.d(TAG,"PBAPClientProfile got connect request");
-        }
+        Log.d(TAG,"PBAPClientProfile got connect request");
         if (mService == null) {
             return false;
         }
-        List<BluetoothDevice> srcs = getConnectedDevices();
-        if (srcs != null) {
-            for (BluetoothDevice src : srcs) {
-                if (src.equals(device)) {
-                    // Connect to same device, Ignore it
-                    Log.d(TAG,"Ignoring Connect");
-                    return true;
-                }
-            }
-        }
         Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
-
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (V) {
-            Log.d(TAG,"PBAPClientProfile got disconnect request");
-        }
+        Log.d(TAG,"PBAPClientProfile got disconnect request");
         if (mService == null) {
             return false;
         }
@@ -218,9 +198,7 @@
     }
 
     protected void finalize() {
-        if (V) {
-            Log.d(TAG, "finalize()");
-        }
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
new file mode 100644
index 0000000..a070b2a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.net;
+
+import android.app.usage.NetworkStatsManager;
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * Loader for retrieving the network stats details for all UIDs.
+ */
+public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> {
+
+    private static final String TAG = "NetworkDetailLoader";
+    private final NetworkStatsManager mNetworkStatsManager;
+    private final TelephonyManager mTelephonyManager;
+    private final long mStart;
+    private final long mEnd;
+    private final int mSubId;
+    private final int mNetworkType;
+
+    private NetworkStatsDetailLoader(Builder builder) {
+        super(builder.mContext);
+        mStart = builder.mStart;
+        mEnd = builder.mEnd;
+        mSubId = builder.mSubId;
+        mNetworkType = builder.mNetworkType;
+        mNetworkStatsManager = (NetworkStatsManager)
+                builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
+        mTelephonyManager =
+                (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        forceLoad();
+    }
+
+    @Override
+    public NetworkStats loadInBackground() {
+        try {
+            return mNetworkStatsManager.queryDetails(
+                    mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception querying network detail.", e);
+            return null;
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        cancelLoad();
+    }
+
+    public static class Builder {
+        private final Context mContext;
+        private long mStart;
+        private long mEnd;
+        private int mSubId;
+        private int mNetworkType;
+
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        public Builder setStartTime(long start) {
+            mStart = start;
+            return this;
+        }
+
+        public Builder setEndTime(long end) {
+            mEnd = end;
+            return this;
+        }
+
+        public Builder setSubscriptionId(int subId) {
+            mSubId = subId;
+            return this;
+        }
+
+        public Builder setNetworkType(int networkType) {
+            mNetworkType = networkType;
+            return this;
+        }
+
+        public NetworkStatsDetailLoader build() {
+            return new NetworkStatsDetailLoader(this);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
index c311337..82bb011 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
@@ -25,6 +25,12 @@
 
 import androidx.loader.content.AsyncTaskLoader;
 
+/**
+ * Deprecated in favor of {@link NetworkStatsDetailLoader}
+ *
+ * @deprecated
+ */
+@Deprecated
 public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
     private static final String KEY_TEMPLATE = "template";
     private static final String KEY_START = "start";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index dde1746..0ca2e87 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -52,6 +52,8 @@
                 @Override
                 public List<ResourcePath> getIncludedResourcePaths() {
                     final List<ResourcePath> paths = super.getIncludedResourcePaths();
+                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
+                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
                     paths.add(resourcePath("file:frameworks/base/core/res/res"));
                     paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 8ac611f..7baded8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -139,7 +139,7 @@
     public void testGetName_validCachedDevice_nameFound() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
-        assertThat(mCachedDeviceManager.findDevice(mDevice1).getName()).isEqualTo(DEVICE_ALIAS_1);
+        assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
     }
 
     /**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
new file mode 100644
index 0000000..c91ee22
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.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 com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class HidDeviceProfileTest {
+
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothHidDevice mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private HidDeviceProfile mProfile;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mProfile = new HidDeviceProfile(RuntimeEnvironment.application,
+                mDeviceManager, mProfileManager);
+        mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+        mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, mService);
+    }
+
+    @Test
+    public void connect_shouldReturnFalse() {
+        assertThat(mProfile.connect(mBluetoothDevice)).isFalse();
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothHidDevice() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
new file mode 100644
index 0000000..c4c48a8
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class MapClientProfileTest {
+
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothMapClient mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private MapClientProfile mProfile;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mProfile = new MapClientProfile(RuntimeEnvironment.application,
+                mDeviceManager, mProfileManager);
+        mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+        mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothMapClient() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothMapClient() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
new file mode 100644
index 0000000..e4a444c
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class PbapClientProfileTest {
+
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothPbapClient mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private PbapClientProfile mProfile;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mProfile = new PbapClientProfile(RuntimeEnvironment.application,
+                mDeviceManager, mProfileManager);
+        mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+        mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothPbapClient() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothPbapClient() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index a501ffa..40e7386 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -77,10 +77,10 @@
 
     @Test
     public void getIcon_hasIconMetadata_returnIcon() {
-        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_info);
+        mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
 
         assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId())
-                .isEqualTo(R.drawable.ic_info);
+                .isEqualTo(android.R.drawable.ic_info);
     }
 
     @Test
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index b047efb..8a3a0b1 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -19,7 +19,6 @@
     android:id="@+id/menu_container"
     android:layout_width="@dimen/navigation_key_width"
     android:layout_height="match_parent"
-    android:focusable="false"
     android:importantForAccessibility="no"
     >
     <!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index 07f0c83..a8960d9 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -19,7 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tile_page"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:paddingStart="@dimen/notification_side_paddings"
     android:paddingEnd="@dimen/notification_side_paddings"
     android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index e96a09b..11a0187 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -19,6 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:layout_weight="1"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index adf1e74..2538612 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -37,7 +37,7 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:layout_gravity="center_vertical"
-            android:textColor="@android:color/white"
+            android:textColor="?attr/wallpaperTextColor"
             android:textSize="16sp"/>
         <ImageView
             android:id="@+id/dismiss"
@@ -49,6 +49,7 @@
             android:layout_marginEnd="2dp"
             android:alpha="0.7"
             android:src="@drawable/ic_close_white"
+            android:tint="?attr/wallpaperTextColor"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/accessibility_desc_close"/>
     </LinearLayout>
diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/packages/SystemUI/res/values-h320dp/config.xml
deleted file mode 100644
index a9c19db..0000000
--- a/packages/SystemUI/res/values-h320dp/config.xml
+++ /dev/null
@@ -1,22 +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.
-*/
--->
-
-<resources>
-    <!-- The number of rows in the QuickSettings -->
-    <integer name="quick_settings_num_rows">2</integer>
-</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 11bd392..47a6c52 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
     <integer name="quick_settings_num_columns">3</integer>
 
     <!-- The number of rows in the QuickSettings -->
-    <integer name="quick_settings_num_rows">1</integer>
+    <integer name="quick_settings_max_rows">3</integer>
 
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">1</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 26eadb5..c168d4e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -955,7 +955,7 @@
     <dimen name="nav_content_padding">0dp</dimen>
     <dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
     <dimen name="nav_quick_scrub_track_thickness">10dp</dimen>
-    <dimen name="nav_home_back_gesture_drag_limit">60dp</dimen>
+    <dimen name="nav_home_back_gesture_drag_limit">40dp</dimen>
 
     <!-- Navigation bar shadow params. -->
     <dimen name="nav_key_button_shadow_offset_x">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1655c01..757c821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,9 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
+import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.READY;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -30,7 +33,6 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
-import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -38,6 +40,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricSourceType;
 import android.media.AudioManager;
 import android.media.SoundPool;
 import android.os.Bundle;
@@ -58,6 +61,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.ViewGroup;
 import android.view.WindowManagerPolicyConstants;
 import android.view.animation.Animation;
@@ -70,21 +74,21 @@
 import com.android.internal.policy.IKeyguardExitCallback;
 import com.android.internal.policy.IKeyguardStateCallback;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardDisplayManager;
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
@@ -274,6 +278,12 @@
 
     private KeyguardUpdateMonitor mUpdateMonitor;
 
+    /**
+     * Last SIM state reported by the telephony system.
+     * Index is the slotId - in case of multiple SIM cards.
+     */
+    private final SparseArray<IccCardConstants.State> mLastSimStates = new SparseArray<>();
+
     private boolean mDeviceInteractive;
     private boolean mGoingToSleep;
 
@@ -450,6 +460,14 @@
                 }
             }
 
+            boolean simWasLocked;
+            synchronized (KeyguardViewMediator.this) {
+                IccCardConstants.State lastState = mLastSimStates.get(slotId);
+                simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
+                    && simState == READY;
+                mLastSimStates.append(slotId, simState);
+            }
+
             switch (simState) {
                 case NOT_READY:
                 case ABSENT:
@@ -503,6 +521,9 @@
                 case READY:
                     synchronized (KeyguardViewMediator.this) {
                         if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+                        if (mShowing && simWasLocked) {
+                            resetStateLocked();
+                        }
                         mLockWhenSimRemoved = true;
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 123fca7..fcd479c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,14 +6,12 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
+
 import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
@@ -41,14 +39,12 @@
         return t * t * t + 1.0f;
     };
 
-
     private final ArrayList<TileRecord> mTiles = new ArrayList<>();
     private final ArrayList<TilePage> mPages = new ArrayList<>();
 
     private PageIndicator mPageIndicator;
     private float mPageIndicatorPosition;
 
-    private int mNumPages;
     private PageListener mPageListener;
 
     private boolean mListening;
@@ -56,6 +52,7 @@
 
     private AnimatorSet mBounceAnimatorSet;
     private float mLastExpansion;
+    private boolean mDistributeTiles = false;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -122,7 +119,7 @@
 
     public void setPageIndicator(PageIndicator indicator) {
         mPageIndicator = indicator;
-        mPageIndicator.setNumPages(mNumPages);
+        mPageIndicator.setNumPages(mPages.size());
         mPageIndicator.setLocation(mPageIndicatorPosition);
     }
 
@@ -136,13 +133,15 @@
     @Override
     public void addTile(TileRecord tile) {
         mTiles.add(tile);
-        postDistributeTiles();
+        mDistributeTiles = true;
+        requestLayout();
     }
 
     @Override
     public void removeTile(TileRecord tile) {
         if (mTiles.remove(tile)) {
-            postDistributeTiles();
+            mDistributeTiles = true;
+            requestLayout();
         }
     }
 
@@ -175,44 +174,50 @@
         mPageListener = listener;
     }
 
-    private void postDistributeTiles() {
-        removeCallbacks(mDistribute);
-        post(mDistribute);
-    }
-
     private void distributeTiles() {
+        emptyAndInflateOrRemovePages();
+
+        final int tileCount = mPages.get(0).maxTiles();
         if (DEBUG) Log.d(TAG, "Distributing tiles");
-        final int NP = mPages.size();
-        for (int i = 0; i < NP; i++) {
-            mPages.get(i).removeAllViews();
-        }
         int index = 0;
         final int NT = mTiles.size();
         for (int i = 0; i < NT; i++) {
             TileRecord tile = mTiles.get(i);
-            if (mPages.get(index).isFull()) {
-                if (++index == mPages.size()) {
-                    if (DEBUG) Log.d(TAG, "Adding page for "
-                            + tile.tile.getClass().getSimpleName());
-                    mPages.add((TilePage) LayoutInflater.from(getContext())
-                            .inflate(R.layout.qs_paged_page, this, false));
-                }
+            if (mPages.get(index).mRecords.size() == tileCount) index++;
+            if (DEBUG) {
+                Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+                        + index);
             }
-            if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
-                    + index);
             mPages.get(index).addTile(tile);
         }
-        if (mNumPages != index + 1) {
-            mNumPages = index + 1;
-            while (mPages.size() > mNumPages) {
-                mPages.remove(mPages.size() - 1);
-            }
-            if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
-            mPageIndicator.setNumPages(mNumPages);
-            setAdapter(mAdapter);
-            mAdapter.notifyDataSetChanged();
-            setCurrentItem(0, false);
+    }
+
+    private void emptyAndInflateOrRemovePages() {
+        final int nTiles = mTiles.size();
+        int numPages = nTiles / mPages.get(0).maxTiles();
+        // Add one more not full page if needed
+        numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
+
+        final int NP = mPages.size();
+        for (int i = 0; i < NP; i++) {
+            mPages.get(i).removeAllViews();
         }
+        if (NP == numPages) {
+            return;
+        }
+        while (mPages.size() < numPages) {
+            if (DEBUG) Log.d(TAG, "Adding page");
+            mPages.add((TilePage) LayoutInflater.from(getContext())
+                    .inflate(R.layout.qs_paged_page, this, false));
+        }
+        while (mPages.size() > numPages) {
+            if (DEBUG) Log.d(TAG, "Removing page");
+            mPages.remove(mPages.size() - 1);
+        }
+        mPageIndicator.setNumPages(mPages.size());
+        setAdapter(mAdapter);
+        mAdapter.notifyDataSetChanged();
+        setCurrentItem(0, false);
     }
 
     @Override
@@ -222,20 +227,39 @@
         setPadding(0, 0, 0,
                 getContext().getResources().getDimensionPixelSize(
                         R.dimen.qs_paged_tile_layout_padding_bottom));
-
         boolean changed = false;
         for (int i = 0; i < mPages.size(); i++) {
             changed |= mPages.get(i).updateResources();
         }
         if (changed) {
-            distributeTiles();
+            mDistributeTiles = true;
+            requestLayout();
         }
         return changed;
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+        final int nTiles = mTiles.size();
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+
+            // Only change the pages if the number of rows or columns (from updateResources) has
+            // changed or the tiles have changed
+            if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+                mDistributeTiles = false;
+                distributeTiles();
+            }
+
+            final int nRows = mPages.get(0).mRows;
+            for (int i = 0; i < mPages.size(); i++) {
+                TilePage t = mPages.get(i);
+                t.mRows = nRows;
+            }
+        }
+
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
         // The ViewPager likes to eat all of the space, instead force it to wrap to the max height
         // of the pages.
         int maxHeight = 0;
@@ -249,13 +273,6 @@
         setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom());
     }
 
-    private final Runnable mDistribute = new Runnable() {
-        @Override
-        public void run() {
-            distributeTiles();
-        }
-    };
-
     public int getColumnCount() {
         if (mPages.size() == 0) return 0;
         return mPages.get(0).mColumns;
@@ -346,33 +363,17 @@
             };
 
     public static class TilePage extends TileLayout {
-        private int mMaxRows = 3;
+
         public TilePage(Context context, AttributeSet attrs) {
             super(context, attrs);
-            updateResources();
-        }
-
-        @Override
-        public boolean updateResources() {
-            final int rows = getRows();
-            boolean changed = rows != mMaxRows;
-            if (changed) {
-                mMaxRows = rows;
-                requestLayout();
-            }
-            return super.updateResources() || changed;
-        }
-
-        private int getRows() {
-            return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows));
-        }
-
-        public void setMaxRows(int maxRows) {
-            mMaxRows = maxRows;
         }
 
         public boolean isFull() {
-            return mRecords.size() >= mColumns * mMaxRows;
+            return mRecords.size() >= mColumns * mRows;
+        }
+
+        public int maxTiles() {
+            return mColumns * mRows;
         }
     }
 
@@ -401,7 +402,7 @@
 
         @Override
         public int getCount() {
-            return mNumPages;
+            return mPages.size();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index feff5d4..1451e71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -91,15 +92,24 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Since we control our own bottom, be whatever size we want.
-        // Otherwise the QSPanel ends up with 0 height when the window is only the
-        // size of the status bar.
-        mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
-                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+        // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
+        // bottom and footer are inside the screen.
+        Configuration config = getResources().getConfiguration();
+        boolean navBelow = config.smallestScreenWidthDp >= 600
+                || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
+        MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+
+        // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
+        // subtract its height. We do not care if the collapsed notifications fit in the screen.
+        int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+                - getPaddingBottom();
+        if (navBelow) {
+            maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+        }
+        mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
         int width = mQSPanel.getMeasuredWidth();
-        LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
         int height = layoutParams.topMargin + layoutParams.bottomMargin
-                + mQSPanel.getMeasuredHeight();
+                + mQSPanel.getMeasuredHeight() + getPaddingBottom();
         super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c67165e..01ff72e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -24,10 +24,12 @@
     protected int mCellMarginHorizontal;
     protected int mCellMarginVertical;
     protected int mSidePadding;
+    protected int mRows = 1;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     private int mCellMarginTop;
     private boolean mListening;
+    protected int mMaxAllowedRows = 3;
 
     public TileLayout(Context context) {
         this(context, null);
@@ -86,6 +88,7 @@
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
         mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
+        mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
         if (mColumns != columns) {
             mColumns = columns;
             requestLayout();
@@ -96,10 +99,16 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
+        // it will show all its tiles. In this case, the tiles have to be entered before the
+        // container is measured. Any change in the tiles, should trigger a remeasure.
         final int numTiles = mRecords.size();
         final int width = MeasureSpec.getSize(widthMeasureSpec)
                 - getPaddingStart() - getPaddingEnd();
-        final int numRows = (numTiles + mColumns - 1) / mColumns;
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightMode == MeasureSpec.UNSPECIFIED) {
+            mRows = (numTiles + mColumns - 1) / mColumns;
+        }
         mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
 
         // Measure each QS tile.
@@ -112,13 +121,35 @@
 
         // Only include the top margin in our measurement if we have more than 1 row to show.
         // Otherwise, don't add the extra margin buffer at top.
-        int height = (mCellHeight + mCellMarginVertical) * numRows +
-                (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
+        int height = (mCellHeight + mCellMarginVertical) * mRows +
+                (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
         if (height < 0) height = 0;
 
         setMeasuredDimension(width, height);
     }
 
+    /**
+     * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
+     * of 1 and a maximum of mMaxAllowedRows.
+     *
+     * @param heightMeasureSpec Available height.
+     * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
+     */
+    public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
+        final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop;
+        final int previousRows = mRows;
+        mRows = availableHeight / (mCellHeight + mCellMarginVertical);
+        if (mRows >= mMaxAllowedRows) {
+            mRows = mMaxAllowedRows;
+        } else if (mRows <= 1) {
+            mRows = 1;
+        }
+        if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+            mRows = (tilesCount + mColumns - 1) / mColumns;
+        }
+        return previousRows != mRows;
+    }
+
     @Override
     public boolean hasOverlappingRendering() {
         return false;
@@ -135,7 +166,8 @@
         int column = 0;
 
         // Layout each QS tile.
-        for (int i = 0; i < numRecords; i++, column++) {
+        final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
+        for (int i = 0; i < tilesToLayout; i++, column++) {
             // If we reached the last column available to layout a tile, wrap back to the next row.
             if (column == mColumns) {
                 column = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index d7abb38..177362c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -16,23 +16,16 @@
 
 package com.android.systemui.recents.events;
 
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Log;
-import android.util.MutableBoolean;
 
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -117,11 +110,7 @@
  * <p>
  * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
  * on the main application thread.  Publishers can send() events to synchronously call subscribers
- * of that event, or post() events to be processed in the next run of the {@link Looper}.  In
- * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
- * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
- * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
- * correctly across processes.
+ * of that event, or post() events to be processed in the next run of the {@link Looper}.
  *
  * <p>
  * Subscribers must be registered with a particular EventBus before they will receive events, and
@@ -135,26 +124,12 @@
  * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
  * </ul>
  *
- * <p>
- * Interprocess-Event method signature:<ul>
- * <li>Methods must be public final
- * <li>Methods must return void
- * <li>Methods must be called "onInterprocessBusEvent"
- * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
- * </ul>
- * </p>
- *
  * </p>
  * Each subscriber can be registered with a given priority (default 1), and events will be dispatch
  * in decreasing order of priority.  For subscribers with the same priority, events will be
  * dispatched by latest registration time to earliest.
  *
  * <p>
- * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
- * takes a {@link Bundle} and implement toBundle().  This allows us to serialize events to be sent
- * across processes.
- *
- * <p>
  * Caveats:<ul>
  * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
  * there must be another strong reference to the publisher for it to not get garbage-collected and
@@ -201,7 +176,7 @@
  * result?
  * </p>
  */
-public class EventBus extends BroadcastReceiver {
+public class EventBus {
 
     private static final String TAG = "EventBus";
     private static final boolean DEBUG_TRACE_ALL = false;
@@ -314,35 +289,6 @@
     }
 
     /**
-     * An inter-process event super class that allows us to track user state across subscriber
-     * invocations.
-     */
-    public static class InterprocessEvent extends Event {
-        private static final String EXTRA_USER = "_user";
-
-        // The user which this event originated from
-        public final int user;
-
-        // Only accessible from derived events
-        protected InterprocessEvent(int user) {
-            this.user = user;
-        }
-
-        /**
-         * Called from the event bus
-         */
-        protected InterprocessEvent(Bundle b) {
-            user = b.getInt(EXTRA_USER);
-        }
-
-        protected Bundle toBundle() {
-            Bundle b = new Bundle();
-            b.putInt(EXTRA_USER, user);
-            return b;
-        }
-    }
-
-    /**
      * Proguard must also know, and keep, all methods matching this signature.
      *
      * -keepclassmembers class ** {
@@ -351,13 +297,6 @@
      * }
      */
     private static final String METHOD_PREFIX = "onBusEvent";
-    private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
-
-    // Ensures that interprocess events can only be sent from a process holding this permission. */
-    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
-
-    // Used for passing event data across process boundaries
-    private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
 
     // The default priority of all subscribers
     private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
@@ -383,10 +322,6 @@
     // The handler to post all events
     private Handler mHandler;
 
-    // Keep track of whether we have registered a broadcast receiver already, so that we can
-    // unregister ourselves before re-registering again with a new IntentFilter.
-    private boolean mHasRegisteredReceiver;
-
     /**
      * Map from event class -> event handler list.  Keeps track of the actual mapping from event
      * to subscriber method.
@@ -401,13 +336,6 @@
     private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
 
     /**
-     * Map from interprocess event name -> interprocess event class.  Used for mapping the event
-     * name after receiving the broadcast, to the event type.  After which a new instance is created
-     * and posted in the local process.
-     */
-    private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
-
-    /**
      * Set of all currently registered subscribers
      */
     private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
@@ -447,7 +375,7 @@
      *                   be scanned for appropriate event handler methods.
      */
     public void register(Object subscriber) {
-        registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
+        registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
     }
 
     /**
@@ -460,44 +388,7 @@
      *                 subscribers
      */
     public void register(Object subscriber, int priority) {
-        registerSubscriber(subscriber, priority, null);
-    }
-
-    /**
-     * Explicitly registers a subscriber to receive interprocess events with the default priority.
-     *
-     * @param subscriber the subscriber to handle events.  If this is the first instance of the
-     *                   subscriber's class type that has been registered, the class's methods will
-     *                   be scanned for appropriate event handler methods.
-     */
-    public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
-        registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
-    }
-
-    /**
-     * Registers a subscriber to receive interprocess events with the given priority.
-     *
-     * @param subscriber the subscriber to handle events.  If this is the first instance of the
-     *                   subscriber's class type that has been registered, the class's methods will
-     *                   be scanned for appropriate event handler methods.
-     * @param priority the priority that this subscriber will receive events relative to other
-     *                 subscribers
-     */
-    public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
-        if (DEBUG_TRACE_ALL) {
-            logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
-        }
-
-        // Register the subscriber normally, and update the broadcast receiver filter if this is
-        // a new subscriber type with interprocess events
-        MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
-        registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
-        if (DEBUG_TRACE_ALL) {
-            logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
-        }
-        if (hasInterprocessEventsChanged.value) {
-            registerReceiverForInterprocessEvents(context);
-        }
+        registerSubscriber(subscriber, priority);
     }
 
     /**
@@ -538,18 +429,6 @@
     }
 
     /**
-     * Explicit unregistration for interprocess event subscribers.  This actually behaves exactly
-     * the same as unregister() since we also do not want to stop listening for specific
-     * inter-process messages in case new instances of that subscriber is registered.
-     */
-    public void unregisterInterprocess(Context context, Object subscriber) {
-        if (DEBUG_TRACE_ALL) {
-            logWithPid("unregisterInterprocess()");
-        }
-        unregister(subscriber);
-    }
-
-    /**
      * Sends an event to the subscribers of the given event type immediately.  This can only be
      * called from the same thread as the EventBus's looper thread (for the default EventBus, this
      * is the main application thread).
@@ -599,57 +478,6 @@
         }
     }
 
-    /** Prevent post()ing an InterprocessEvent */
-    @Deprecated
-    public void post(InterprocessEvent event) {
-        throw new RuntimeException("Not supported, use postInterprocess");
-    }
-
-    /** Prevent send()ing an InterprocessEvent */
-    @Deprecated
-    public void send(InterprocessEvent event) {
-        throw new RuntimeException("Not supported, use postInterprocess");
-    }
-
-    /**
-     * Posts an interprocess event.
-     */
-    public void postInterprocess(Context context, final InterprocessEvent event) {
-        if (DEBUG_TRACE_ALL) {
-            logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
-        }
-        String eventType = event.getClass().getName();
-        Bundle eventBundle = event.toBundle();
-        Intent intent = new Intent(eventType);
-        intent.setPackage(context.getPackageName());
-        intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
-                Intent.FLAG_RECEIVER_FOREGROUND);
-        context.sendBroadcastAsUser(intent, UserHandle.ALL);
-    }
-
-    /**
-     * Receiver for interprocess events.
-     */
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (DEBUG_TRACE_ALL) {
-            logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
-        }
-
-        Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
-        Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
-        try {
-            Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
-            send((Event) ctor.newInstance(eventBundle));
-        } catch (NoSuchMethodException|
-                InvocationTargetException|
-                InstantiationException|
-                IllegalAccessException e) {
-            Log.e(TAG, "Failed to create InterprocessEvent", e.getCause());
-        }
-    }
-
     /**
      * @return a dump of the current state of the EventBus
      */
@@ -711,8 +539,7 @@
     /**
      * Registers a new subscriber.
      */
-    private void registerSubscriber(Object subscriber, int priority,
-            MutableBoolean hasInterprocessEventsChangedOut) {
+    private void registerSubscriber(Object subscriber, int priority) {
         // Fail immediately if we are being called from the non-main thread
         long callingThreadId = Thread.currentThread().getId();
         if (callingThreadId != mHandler.getLooper().getThread().getId()) {
@@ -759,32 +586,16 @@
         }
 
         // Find all the valid event bus handler methods of the subscriber
-        MutableBoolean isInterprocessEvent = new MutableBoolean(false);
         Method[] methods = subscriberType.getDeclaredMethods();
         for (Method m : methods) {
             Class<?>[] parameterTypes = m.getParameterTypes();
-            isInterprocessEvent.value = false;
-            if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
+            if (isValidEventBusHandlerMethod(m, parameterTypes)) {
                 Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
                 ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
                 if (eventTypeHandlers == null) {
                     eventTypeHandlers = new ArrayList<>();
                     mEventTypeMap.put(eventType, eventTypeHandlers);
                 }
-                if (isInterprocessEvent.value) {
-                    try {
-                        // Enforce that the event must have a Bundle constructor
-                        eventType.getConstructor(Bundle.class);
-
-                        mInterprocessEventNameMap.put(eventType.getName(),
-                                (Class<? extends InterprocessEvent>) eventType);
-                        if (hasInterprocessEventsChangedOut != null) {
-                            hasInterprocessEventsChangedOut.value = true;
-                        }
-                    } catch (NoSuchMethodException e) {
-                        throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
-                    }
-                }
                 EventHandlerMethod method = new EventHandlerMethod(m, eventType);
                 EventHandler handler = new EventHandler(sub, method, priority);
                 eventTypeHandlers.add(handler);
@@ -793,8 +604,7 @@
 
                 if (DEBUG_TRACE_ALL) {
                     logWithPid("  * Method: " + m.getName() +
-                            " event: " + parameterTypes[0].getSimpleName() +
-                            " interprocess? " + isInterprocessEvent.value);
+                            " event: " + parameterTypes[0].getSimpleName());
                 }
             }
         }
@@ -830,12 +640,7 @@
             final EventHandler eventHandler = eventHandlers.get(i);
             if (eventHandler.subscriber.getReference() != null) {
                 if (event.requiresPost) {
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            processEvent(eventHandler, event);
-                        }
-                    });
+                    mHandler.post(() -> processEvent(eventHandler, event));
                     hasPostedEvent = true;
                 } else {
                     processEvent(eventHandler, event);
@@ -845,12 +650,7 @@
 
         // Clean up after this event, deferring until all subscribers have been called
         if (hasPostedEvent) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    event.onPostDispatch();
-                }
-            });
+            mHandler.post(event::onPostDispatch);
         } else {
             event.onPostDispatch();
         }
@@ -898,29 +698,6 @@
     }
 
     /**
-     * Re-registers the broadcast receiver for any new messages that we want to listen for.
-     */
-    private void registerReceiverForInterprocessEvents(Context context) {
-        if (DEBUG_TRACE_ALL) {
-            logWithPid("registerReceiverForInterprocessEvents()");
-        }
-        // Rebuild the receiver filter with the new interprocess events
-        IntentFilter filter = new IntentFilter();
-        for (String eventName : mInterprocessEventNameMap.keySet()) {
-            filter.addAction(eventName);
-            if (DEBUG_TRACE_ALL) {
-                logWithPid("  filter: " + eventName);
-            }
-        }
-        // Re-register the receiver with the new filter
-        if (mHasRegisteredReceiver) {
-            context.unregisterReceiver(this);
-        }
-        context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
-        mHasRegisteredReceiver = true;
-    }
-
-    /**
      * Returns whether this subscriber is currently registered.  If {@param removeFoundSubscriber}
      * is true, then remove the subscriber before returning.
      */
@@ -940,28 +717,19 @@
     /**
      * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
      */
-    private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
-            MutableBoolean isInterprocessEventOut) {
+    private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes) {
         int modifiers = method.getModifiers();
         if (Modifier.isPublic(modifiers) &&
                 Modifier.isFinal(modifiers) &&
                 method.getReturnType().equals(Void.TYPE) &&
                 parameterTypes.length == 1) {
-            if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
-                    method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
-                isInterprocessEventOut.value = true;
-                return true;
-            } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
+            if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
                             method.getName().startsWith(METHOD_PREFIX)) {
-                isInterprocessEventOut.value = false;
                 return true;
             } else {
                 if (DEBUG_TRACE_ALL) {
                     if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
                         logWithPid("  Expected method take an Event-based parameter: " + method.getName());
-                    } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
-                            !method.getName().startsWith(METHOD_PREFIX)) {
-                        logWithPid("  Expected method start with method prefix: " + method.getName());
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f7615e6..d8f7b61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -121,7 +121,8 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class)
+                .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 7a69f2e..d6719f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -16,42 +16,70 @@
 
 package com.android.systemui.statusbar;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.util.ArraySet;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.Comparator;
 
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
 public class StatusBarStateController {
+    private static final String TAG = "SbStateController";
 
     private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
     private static final int MIN_STATE = StatusBarState.SHADE;
 
-    private final ArraySet<StateListener> mListeners = new ArraySet<>();
+    private static final Comparator <RankedListener> mComparator
+            = (o1, o2) -> Integer.compare(o1.rank, o2.rank);
+
+    private final ArrayList<RankedListener> mListeners = new ArrayList<>();
     private int mState;
     private int mLastState;
     private boolean mLeaveOpenOnKeyguardHide;
 
+    // TODO: b/115739177 (remove this explicit ordering if we can)
+    @Retention(SOURCE)
+    @IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF})
+    public @interface SbStateListenerRank {}
+    // This is the set of known dependencies when updating StatusBarState
+    public static final int RANK_STATUS_BAR = 0;
+    public static final int RANK_STATUS_BAR_WINDOW_CONTROLLER = 1;
+    public static final int RANK_STACK_SCROLLER = 2;
+    public static final int RANK_SHELF = 3;
+
     public int getState() {
         return mState;
     }
 
-    public void setState(int state) {
+    public boolean setState(int state) {
         if (state > MAX_STATE || state < MIN_STATE) {
             throw new IllegalArgumentException("Invalid state " + state);
         }
         if (state == mState) {
-            return;
+            return false;
         }
         synchronized (mListeners) {
-            for (StateListener listener : new ArraySet<>(mListeners)) {
-                listener.onStatePreChange(mState, state);
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onStatePreChange(mState, state);
             }
             mLastState = mState;
             mState = state;
-            for (StateListener listener : new ArraySet<>(mListeners)) {
-                listener.onStateChanged(mState);
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onStateChanged(mState);
+            }
+
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onStatePostChange();
             }
         }
+
+        return true;
     }
 
     public boolean goingToFullShade() {
@@ -72,20 +100,67 @@
 
     public void addListener(StateListener listener) {
         synchronized (mListeners) {
-            mListeners.add(listener);
+            addListenerInternalLocked(listener, Integer.MAX_VALUE);
         }
     }
 
+    /**
+     * Add a listener and a rank based on the priority of this message
+     * @param listener the listener
+     * @param rank the order in which you'd like to be called. Ranked listeners will be
+     * notified before unranked, and we will sort ranked listeners from low to high
+     *
+     * @deprecated This method exists only to solve latent inter-dependencies from refactoring
+     * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
+     * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+     */
+    public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+        synchronized (mListeners) {
+            addListenerInternalLocked(listener, rank);
+        }
+    }
+
+    @GuardedBy("mListeners")
+    private void addListenerInternalLocked(StateListener listener, int rank) {
+        // Protect against double-subscribe
+        for (RankedListener rl : mListeners) {
+            if (rl.listener.equals(listener)) {
+                return;
+            }
+        }
+
+        RankedListener rl = new RankedListener(listener, rank);
+        mListeners.add(rl);
+        mListeners.sort(mComparator);
+    }
+
     public void removeListener(StateListener listener) {
         synchronized (mListeners) {
-            mListeners.remove(listener);
+            mListeners.removeIf((it) -> it.listener.equals(listener));
         }
     }
 
+    public static String describe(int state) {
+        return StatusBarState.toShortString(state);
+    }
+
     public interface StateListener {
         public default void onStatePreChange(int oldState, int newState) {
         }
 
+        public default void onStatePostChange() {
+        }
+
         public void onStateChanged(int newState);
     }
+
+    private class RankedListener {
+        private final StateListener listener;
+        private final int rank;
+
+        private RankedListener(StateListener l, int r) {
+            listener = l;
+            rank = r;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
index f204c42..5ad2ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
@@ -14,7 +14,10 @@
  * limitations under the License
  */
 
-package java.lang.annotation;
+package com.android.systemui.statusbar.notification;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 @Retention(RetentionPolicy.SOURCE)
 public @interface ShadeViewRefactor {
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 52844fe..da98565 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
@@ -115,6 +115,8 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -134,8 +136,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.annotation.ShadeViewRefactor;
-import java.lang.annotation.ShadeViewRefactor.RefactorComponent;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -620,7 +620,8 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class)
+                .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
@@ -4248,8 +4249,9 @@
         mDimAnimator.addUpdateListener(mDimUpdateListener);
         mDimAnimator.start();
     }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setHideSensitive(boolean hideSensitive, boolean animate) {
+    private void setHideSensitive(boolean hideSensitive, boolean animate) {
         if (hideSensitive != mAmbientState.isHideSensitive()) {
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
@@ -5008,8 +5010,12 @@
     private void setStatusBarState(int statusBarState) {
         mStatusBarState = statusBarState;
         mAmbientState.setStatusBarState(statusBarState);
+    }
+
+    private void onStatePostChange() {
         boolean onKeyguard = onKeyguard();
         boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+
         if (mHeadsUpAppearanceController != null) {
             mHeadsUpAppearanceController.setPublicMode(publicMode);
         }
@@ -5026,6 +5032,8 @@
         updateFooter();
         updateChildren();
         onUpdateRowStates();
+
+        mEntryManager.updateNotifications();
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5949,5 +5957,10 @@
         public void onStateChanged(int newState) {
             setStatusBarState(newState);
         }
-    };
+
+      @Override
+      public void onStatePostChange() {
+          NotificationStackScrollLayout.this.onStatePostChange();
+      }
+  };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 235ff2b..c32dcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -67,6 +67,10 @@
         mView.setBackground(mBarBackground);
     }
 
+    public void destroy() {
+        // To be overridden
+    }
+
     public int getMode() {
         return mMode;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 81b596c..424acfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -34,9 +34,11 @@
     private val inCarMode: Boolean
     private var uiMode: Int = 0
     private var localeList: LocaleList? = null
+    private val context: Context
 
     init {
         val currentConfig = context.resources.configuration
+        this.context = context
         fontScale = currentConfig.fontScale
         density = currentConfig.densityDpi
         inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
@@ -82,6 +84,10 @@
         }
 
         if (uiModeChanged) {
+            // We need to force the style re-evaluation to make sure that it's up to date
+            // and attrs were reloaded.
+            context.theme.applyStyle(context.themeResId, true)
+
             this.uiMode = uiMode
             listeners.filterForEach({ this.listeners.contains(it) }) {
                 it.onUiModeChanged()
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 10b019d..66486ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -307,7 +307,10 @@
     @Override
     public void onDestroyView() {
         super.onDestroyView();
-        mNavigationBarView.getLightTransitionsController().destroy(getContext());
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getBarTransitions().destroy();
+            mNavigationBarView.getLightTransitionsController().destroy(getContext());
+        }
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
         getContext().unregisterReceiver(mBroadcastReceiver);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index e09d31c..d58b554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -44,6 +44,17 @@
     private boolean mAutoDim;
     private View mNavButtons;
 
+    private final Handler mHandler = Handler.getMain();
+    private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
+            new IWallpaperVisibilityListener.Stub() {
+        @Override
+        public void onWallpaperVisibilityChanged(boolean newVisibility,
+        int displayId) throws RemoteException {
+            mWallpaperVisible = newVisibility;
+            mHandler.post(() -> applyLightsOut(true, false));
+        }
+    };
+
     public NavigationBarTransitions(NavigationBarView view) {
         super(view, R.drawable.nav_background);
         mView = view;
@@ -55,17 +66,9 @@
                 .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
 
         IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
-        Handler handler = Handler.getMain();
         try {
             mWallpaperVisible = windowManagerService.registerWallpaperVisibilityListener(
-                new IWallpaperVisibilityListener.Stub() {
-                    @Override
-                    public void onWallpaperVisibilityChanged(boolean newVisibility,
-                            int displayId) throws RemoteException {
-                        mWallpaperVisible = newVisibility;
-                        handler.post(() -> applyLightsOut(true, false));
-                    }
-                }, Display.DEFAULT_DISPLAY);
+                    mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
         } catch (RemoteException e) {
         }
         mView.addOnLayoutChangeListener(
@@ -88,6 +91,16 @@
     }
 
     @Override
+    public void destroy() {
+        IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
+        try {
+            windowManagerService.unregisterWallpaperVisibilityListener(mWallpaperVisibilityListener,
+                    Display.DEFAULT_DISPLAY);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
     public void setAutoDim(boolean autoDim) {
         if (mAutoDim == autoDim) return;
         mAutoDim = autoDim;
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 2141016..9d13ea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -615,6 +615,7 @@
                         ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
         getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
         getImeSwitchButton().setImageDrawable(mImeIcon);
+        updateContextualContainerVisibility();
 
         // Update menu button, visibility logic in method
         setMenuVisibility(mShowMenu, true);
@@ -796,6 +797,7 @@
                 ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0);
 
         getMenuButton().setVisibility(shouldShow ? View.VISIBLE : View.INVISIBLE);
+        updateContextualContainerVisibility();
     }
 
     public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
@@ -810,6 +812,7 @@
 
         getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
         getAccessibilityButton().setLongClickable(longClickable);
+        updateContextualContainerVisibility();
     }
 
     public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) {
@@ -839,6 +842,7 @@
 
         getRotateSuggestionButton().setVisibility(vis);
         mShowRotateButton = visible;
+        updateContextualContainerVisibility();
 
         // Stop any active animations if hidden
         if (!visible && mRotateSuggestionIcon.canAnimate()) {
@@ -855,6 +859,13 @@
 
     public boolean isRotateButtonVisible() { return mShowRotateButton; }
 
+    private void updateContextualContainerVisibility() {
+        // Only show the menu container when one of its buttons are visible
+        getMenuContainer().setVisibility((mShowAccessibilityButton || mShowRotateButton || mShowMenu
+                || (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0)
+                ? VISIBLE : INVISIBLE);
+    }
+
     void hideRecentsOnboarding() {
         mRecentsOnboarding.hide(true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
index 7b9ed88..09833d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
@@ -98,7 +98,6 @@
         return mClickableChildren
                 .stream()
                 .filter(v -> v.isAttachedToWindow())
-                .filter(v -> v.isFocusable())
                 .map(v -> new Pair<>(distance(v, event), v))
                 .min(Comparator.comparingInt(f -> f.first))
                 .get().second;
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 bd4d790..30e6afa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -77,6 +77,8 @@
     /** Experiment to swipe home button left to execute a back key press */
     private static final String PULL_HOME_GO_BACK_PROP = "persist.quickstepcontroller.homegoesback";
     private static final String HIDE_BACK_BUTTON_PROP = "persist.quickstepcontroller.hideback";
+    private static final String BACK_AFTER_END_PROP
+            = "persist.quickstepcontroller.homegoesbackwhenend";
     private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
     private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
     private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
@@ -84,9 +86,6 @@
     /** When the home-swipe-back gesture is disallowed, make it harder to pull */
     private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
 
-    /** When dragging the home button too far during back gesture, make it harder to pull */
-    private static final float EXCEED_DRAG_HOME_DAMPING_FACTOR = 0.33f;
-
     private NavigationBarView mNavigationBarView;
 
     private boolean mQuickScrubActive;
@@ -361,7 +360,7 @@
                         // Once the user drags the home button past a certain limit, the distance
                         // will lessen as the home button dampens showing that it was pulled too far
                         float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
-                                * EXCEED_DRAG_HOME_DAMPING_FACTOR;
+                                * DISALLOW_GESTURE_DAMPING_FACTOR;
                         diff = (int)(distanceAfterDragLimit + mHomeBackGestureDragLimit);
                         if (mDragPositive) {
                             diff *= -1;
@@ -580,15 +579,21 @@
         if (!mBackGestureActive) {
             mBackGestureActive = true;
             mNavigationBarView.getHomeButton().abortCurrentGesture();
+            final boolean runBackMidGesture
+                    = !SystemProperties.getBoolean(BACK_AFTER_END_PROP, false);
             if (mCanPerformBack) {
                 if (!shouldhideBackButton()) {
                     mNavigationBarView.getBackButton().setAlpha(0 /* alpha */, true /* animate */,
                             BACK_BUTTON_FADE_OUT_ALPHA);
                 }
-                performBack();
+                if (runBackMidGesture) {
+                    performBack();
+                }
             }
             mHandler.removeCallbacks(mExecuteBackRunnable);
-            mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+            if (runBackMidGesture) {
+                mHandler.postDelayed(mExecuteBackRunnable, BACK_GESTURE_POLL_TIMEOUT);
+            }
         }
     }
 
@@ -609,6 +614,9 @@
                 mNavigationBarView.getBackButton().setAlpha(
                         mOverviewEventSender.getBackButtonAlpha(), true /* animate */);
             }
+            if (SystemProperties.getBoolean(BACK_AFTER_END_PROP, false)) {
+                performBack();
+            }
         }
     }
 
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 7316c02dd..759e8ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -632,7 +632,7 @@
 
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mColorExtractor.addOnColorsChangedListener(this);
-        mStatusBarStateController.addListener(this);
+        mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
 
@@ -1144,11 +1144,6 @@
 
     @Override
     public void onUiModeChanged() {
-        // UiMode will change the style was already evaluated.
-        // We need to force the re-evaluation to make sure that all parents
-        // are up to date and new attrs will be rettrieved.
-        mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
-
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.onUiModeChanged();
         }
@@ -3458,7 +3453,14 @@
         mIsKeyguard = false;
         Trace.beginSection("StatusBar#hideKeyguard");
         boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
-        mStatusBarStateController.setState(StatusBarState.SHADE);
+        if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+            //TODO: StatusBarStateController should probably know about hiding the keyguard and
+            // notify listeners.
+
+            // If the state didn't change, we may still need to update public mode
+            updatePublicMode();
+            mEntryManager.updateNotifications();
+        }
         View viewToClick = null;
         if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
             if (!mKeyguardRequested) {
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 e8389af..3db1456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -460,6 +460,11 @@
                 boolean staying = mStatusBar.hideKeyguard();
                 if (!staying) {
                     mStatusBarWindowController.setKeyguardFadingAway(true);
+                    // hide() will happen asynchronously and might arrive after the scrims
+                    // were already hidden, this means that the transition callback won't
+                    // be triggered anymore and StatusBarWindowController will be forever in
+                    // the fadingAway state.
+                    mStatusBar.updateScrimController();
                     wakeAndUnlockDejank();
                 } else {
                     mStatusBar.finishKeyguardFadingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 167bba6..57c7e28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -93,7 +93,8 @@
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
         mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addListener(
+                mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
@@ -135,6 +136,7 @@
         mWindowManager.addView(mStatusBarView, mLp);
         mLpChanged = new WindowManager.LayoutParams();
         mLpChanged.copyFrom(mLp);
+        onThemeChanged();
     }
 
     public void setDozeScreenBrightness(int value) {
@@ -483,6 +485,10 @@
 
     @Override
     public void onThemeChanged() {
+        if (mStatusBarView == null) {
+            return;
+        }
+
         StatusBarStateController state = Dependency.get(StatusBarStateController.class);
         int which;
         if (state.getState() == StatusBarState.KEYGUARD
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 52b813f..7dd0d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -586,7 +586,8 @@
             final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
 
             if (mShowImeOnInputConnection && inputConnection != null) {
-                final InputMethodManager imm = InputMethodManager.getInstance();
+                final InputMethodManager imm =
+                        getContext().getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     // onCreateInputConnection is called by InputMethodManager in the middle of
                     // setting up the connection to the IME; wait with requesting the IME until that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0adb439..29a6b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -403,7 +403,7 @@
                 boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
                 return new Pair<Integer, Boolean>(userId[0], hasCACerts);
             } catch (RemoteException | InterruptedException | AssertionError e) {
-                Log.i(TAG, e.getMessage());
+                Log.i(TAG, "failed to get CA certs", e);
                 new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
                         () -> new CACertLoader().execute(userId[0]),
                         CA_CERT_LOADING_RETRY_TIME_IN_MS);
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 ce0bd58..1e3d42b 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
@@ -19,6 +19,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -316,7 +317,7 @@
     private void setBarStateForTest(int state) {
         ArgumentCaptor<StatusBarStateController.StateListener> captor =
                 ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-        verify(mBarState).addListener(captor.capture());
+        verify(mBarState, atLeastOnce()).addListener(captor.capture());
         captor.getValue().onStateChanged(state);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
index 2423e14..667a508 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
@@ -171,23 +171,6 @@
         ev.recycle();
     }
 
-    @Test
-    public void testFurtherSelectedWhenCloserNotFocusable() {
-        View closer = mockViewAt(0, 0, 10, 10);
-        View further = mockViewAt(20, 0, 10, 10);
-        closer.setFocusable(false);
-
-        mNearestTouchFrame.addView(closer);
-        mNearestTouchFrame.addView(further);
-        mNearestTouchFrame.onMeasure(0, 0);
-
-        MotionEvent ev = MotionEvent.obtain(0, 0, 0,
-                12 /* x */, 5 /* y */, 0);
-        mNearestTouchFrame.onTouchEvent(ev);
-        verify(further).onTouchEvent(eq(ev));
-        ev.recycle();
-    }
-
     private View mockViewAt(int x, int y, int width, int height) {
         View v = spy(new View(mContext));
         doAnswer(invocation -> {
@@ -204,7 +187,6 @@
         v.setRight(width);
         v.setTop(0);
         v.setBottom(height);
-        v.setFocusable(true);
         return v;
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index f8223f6..f7a7e04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -83,4 +84,16 @@
                 & WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
         assertThat(flag).isEqualTo(0);
     }
+
+    @Test
+    public void testOnThemeChanged_doesntCrash() {
+        mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
+                mActivityManager, mDozeParameters);
+        mStatusBarWindowController.onThemeChanged();
+    }
+
+    @Test
+    public void testAdd_updatesVisibilityFlags() {
+        verify(mStatusBarView).setSystemUiVisibility(anyInt());
+    }
 }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b71d7a7..da52d40 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1834,7 +1834,7 @@
 
                 if (provider.widgets.isEmpty()) {
                     // cancel the future updates
-                    cancelBroadcasts(provider);
+                    cancelBroadcastsLocked(provider);
 
                     // send the broacast saying that the provider is not in use any more
                     sendDisabledIntentLocked(provider);
@@ -1843,18 +1843,16 @@
         }
     }
 
-    private void cancelBroadcasts(Provider provider) {
+    private void cancelBroadcastsLocked(Provider provider) {
         if (DEBUG) {
-            Slog.i(TAG, "cancelBroadcasts() for " + provider);
+            Slog.i(TAG, "cancelBroadcastsLocked() for " + provider);
         }
         if (provider.broadcast != null) {
-            mAlarmManager.cancel(provider.broadcast);
-            long token = Binder.clearCallingIdentity();
-            try {
-                provider.broadcast.cancel();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            final PendingIntent broadcast = provider.broadcast;
+            mSaveStateHandler.post(() -> {
+                    mAlarmManager.cancel(broadcast);
+                    broadcast.cancel();
+            });
             provider.broadcast = null;
         }
     }
@@ -2315,7 +2313,7 @@
         mProviders.remove(provider);
 
         // no need to send the DISABLE broadcast, since the receiver is gone anyway
-        cancelBroadcasts(provider);
+        cancelBroadcastsLocked(provider);
     }
 
     private void sendEnableIntentLocked(Provider p) {
@@ -2369,17 +2367,14 @@
                 Binder.restoreCallingIdentity(token);
             }
             if (!alreadyRegistered) {
-                long period = provider.info.updatePeriodMillis;
-                if (period < MIN_UPDATE_PERIOD) {
-                    period = MIN_UPDATE_PERIOD;
-                }
-                final long oldId = Binder.clearCallingIdentity();
-                try {
+                // Set the alarm outside of our locks; we've latched the first-time
+                // invariant and established the PendingIntent safely.
+                final long period = Math.max(provider.info.updatePeriodMillis, MIN_UPDATE_PERIOD);
+                final PendingIntent broadcast = provider.broadcast;
+                mSaveStateHandler.post(() ->
                     mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            SystemClock.elapsedRealtime() + period, period, provider.broadcast);
-                } finally {
-                    Binder.restoreCallingIdentity(oldId);
-                }
+                            SystemClock.elapsedRealtime() + period, period, broadcast)
+                );
             }
         }
     }
@@ -3382,7 +3377,7 @@
                             // Reschedule for the new updatePeriodMillis (don't worry about handling
                             // it specially if updatePeriodMillis didn't change because we just sent
                             // an update, and the next one will be updatePeriodMillis from now).
-                            cancelBroadcasts(provider);
+                            cancelBroadcastsLocked(provider);
                             registerForBroadcastsLocked(provider, appWidgetIds);
                             // If it's currently showing, call back with the new
                             // AppWidgetProviderInfo.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index ad80cc26..2671327 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -587,6 +587,7 @@
             }
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
+                if (sVerbose) Slog.v(LOG_TAG, "calling onFillRequest() for id=" + mRequest.getId());
                 try {
                     remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
@@ -659,6 +660,7 @@
         public void run() {
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
+                if (sVerbose) Slog.v(LOG_TAG, "calling onSaveRequest()");
                 try {
                     remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
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 9af952d..c933833 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -105,7 +105,7 @@
                 try {
                     IBackupTransport transport =
                             transportClient.connectOrThrow(
-                                    "AppBackupUtils.appIsEligibleForBackupAtRuntime");
+                                    "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
                     return transport.isAppEligibleForBackup(
                             packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo));
                 } catch (Exception e) {
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 51d95a2..872261a 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -202,6 +202,15 @@
                     reset();
                     pw.println("binder_calls_stats reset.");
                     return;
+                } else if ("--enable".equals(arg)) {
+                    Binder.setObserver(mBinderCallsStats);
+                    return;
+                } else if ("--disable".equals(arg)) {
+                    Binder.setObserver(null);
+                    return;
+                } else if ("--no-sampling".equals(arg)) {
+                    mBinderCallsStats.setSamplingInterval(1);
+                    return;
                 } else if ("--enable-detailed-tracking".equals(arg)) {
                     SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
                     mBinderCallsStats.setDetailedTracking(true);
@@ -215,6 +224,9 @@
                 } else if ("-h".equals(arg)) {
                     pw.println("binder_calls_stats commands:");
                     pw.println("  --reset: Reset stats");
+                    pw.println("  --enable: Enable tracking binder calls");
+                    pw.println("  --disable: Disables tracking binder calls");
+                    pw.println("  --no-sampling: Tracks all calls");
                     pw.println("  --enable-detailed-tracking: Enables detailed tracking");
                     pw.println("  --disable-detailed-tracking: Disables detailed tracking");
                     return;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 380f6a7..a69d416 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1490,23 +1490,19 @@
         }
     }
 
-    private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+    private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
 
     private void enforceTunnelPermissions(String callingPackage) {
         checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
-        if (false) { // STOPSHIP if this line is present
-            switch (getAppOpsManager().noteOp(
-                        TUNNEL_OP,
-                        Binder.getCallingUid(), callingPackage)) {
-                case AppOpsManager.MODE_DEFAULT:
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
-                    break;
-                case AppOpsManager.MODE_ALLOWED:
-                    return;
-                default:
-                    throw new SecurityException("Request to ignore AppOps for non-legacy API");
-            }
+        switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+            case AppOpsManager.MODE_DEFAULT:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+                break;
+            case AppOpsManager.MODE_ALLOWED:
+                return;
+            default:
+                throw new SecurityException("Request to ignore AppOps for non-legacy API");
         }
     }
 
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index ab7bf28..ee01d86 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -189,6 +189,10 @@
             } else if ("reset".equals(cmd)) {
                 mStats.reset();
                 return 0;
+            } else if ("sampling_interval".equals(cmd)) {
+                int sampling = Integer.parseUnsignedInt(getNextArgRequired());
+                setSamplingInterval(sampling);
+                return 0;
             } else {
                 return handleDefaultCommands(cmd);
             }
@@ -198,9 +202,10 @@
         public void onHelp() {
             final PrintWriter pw = getOutPrintWriter();
             pw.println(LOOPER_STATS_SERVICE_NAME + " commands:");
-            pw.println("  enable: Enable collecting stats");
-            pw.println("  disable: Disable collecting stats");
-            pw.println("  reset: Reset stats");
+            pw.println("  enable: Enable collecting stats.");
+            pw.println("  disable: Disable collecting stats.");
+            pw.println("  sampling_interval: Change the sampling interval.");
+            pw.println("  reset: Reset stats.");
         }
     }
 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index cb03255..f436286 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -42,31 +42,25 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.Sandman;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
 import android.util.Slog;
 
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-
 import com.android.internal.R;
 import com.android.internal.app.DisableCarModeActivity;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.DumpUtils;
-import com.android.server.power.ShutdownThread;
 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;
+
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -466,7 +460,7 @@
             uiMode |= mNightMode << 4;
         }
 
-        if (mPowerSave && !mNightModeLocked) {
+        if (mPowerSave) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         }
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 1866420..01421c7 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -126,11 +126,6 @@
 
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
-    @VisibleForTesting
-    ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
-        this(supervisor, supervisor.mDisplayManager.getDisplay(displayId));
-    }
-
     ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
         mSupervisor = supervisor;
         mDisplayId = display.getDisplayId();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7533db1..b898a90 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8406,10 +8406,17 @@
         }
     }
 
+    /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
+    @Deprecated
+    @Override
     public void removeContentProviderExternal(String name, IBinder token) {
+        removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
         enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
             "Do not have permission in call removeContentProviderExternal()");
-        int userId = UserHandle.getCallingUserId();
         long ident = Binder.clearCallingIdentity();
         try {
             removeContentProviderExternalUnchecked(name, token, userId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 628207c..2199bb7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,18 +17,18 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
 import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
 import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
 import static android.app.ActivityOptions.ANIM_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -81,6 +81,8 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
@@ -93,6 +95,13 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -110,14 +119,6 @@
 import static com.android.server.am.TaskPersister.DEBUG;
 import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -132,6 +133,7 @@
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.ResultInfo;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ClientTransaction;
@@ -143,7 +145,6 @@
 import android.app.servertransaction.PipModeChangeItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.WindowVisibilityItem;
-import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -1279,20 +1280,14 @@
 
     /**
      * Check whether this activity can be launched on the specified display.
+     *
      * @param displayId Target display id.
-     * @return {@code true} if either it is the default display or this activity is resizeable and
-     *         can be put a secondary screen.
+     * @return {@code true} if either it is the default display or this activity can be put on a
+     *         secondary screen.
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
-        final TaskRecord task = getTask();
-
-        // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
-        // This allows for a non resizable activity to be launched into a resizeable task.
-        final boolean resizeable =
-                task != null ? task.isResizeable() : supportsResizeableMultiWindow();
-
-        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
-                resizeable, launchedFromPid, launchedFromUid, info);
+        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+                launchedFromUid, info);
     }
 
     /**
@@ -2495,6 +2490,14 @@
         }
     }
 
+    /**
+     * @return {@code true} if this activity was reparented to another display but
+     *         {@link #ensureActivityConfiguration} is not called.
+     */
+    boolean shouldUpdateConfigForDisplayChanged() {
+        return mLastReportedDisplayId != getDisplayId();
+    }
+
     boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
         return ensureActivityConfiguration(globalChanges, preserveWindow,
                 false /* ignoreStopState */);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aa4e68d..91e677b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5375,12 +5375,14 @@
         display.positionChildAtTop(this, false /* includingParents */);
     }
 
+    /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
     void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
             boolean setPause, String reason) {
         if (!moveToFront) {
             return;
         }
 
+        final ActivityState origState = r.getState();
         // If the activity owns the last resumed activity, transfer that together,
         // so that we don't resume the same activity again in the new stack.
         // Apps may depend on onResume()/onPause() being called in pairs.
@@ -5393,9 +5395,14 @@
             mPausingActivity = r;
             schedulePauseTimeout(r);
         }
-        // Move the stack in which we are placing the activity to the front. The call will also
-        // make sure the activity focus is set.
+        // Move the stack in which we are placing the activity to the front.
         moveToFront(reason);
+        // If the original state is resumed, there is no state change to update focused app.
+        // So here makes sure the activity focus is set if it is the top.
+        if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+            // TODO(b/111361570): Support multiple focused apps in WM
+            mService.setResumedActivityUncheckLocked(r, reason);
+        }
     }
 
     public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 310898e..d096fed 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -488,8 +488,8 @@
     }
 
     /** Check if placing task or activity on specified display is allowed. */
-    boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
-            int callingUid, ActivityInfo activityInfo) {
+    boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+            ActivityInfo activityInfo) {
         if (displayId == DEFAULT_DISPLAY) {
             // No restrictions for the default display.
             return true;
@@ -498,10 +498,6 @@
             // Can't launch on secondary displays if feature is not supported.
             return false;
         }
-        if (!resizeable && !displayConfigMatchesGlobal(displayId)) {
-            // Can't apply wrong configuration to non-resizeable activities.
-            return false;
-        }
         if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
             // Can't place activities to a display that has restricted launch rules.
             // In this case the request should be made by explicitly adding target display id and
@@ -690,7 +686,7 @@
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
         final Display[] displays = mDisplayManager.getDisplays();
-        for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
             final Display display = displays[displayNdx];
             final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
             if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
@@ -4530,11 +4526,14 @@
             mService.setTaskWindowingMode(task.taskId,
                     WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
             if (preferredDisplayId != actualDisplayId) {
-                // Display a warning toast that we tried to put a non-resizeable task on a secondary
-                // display with config different from global config.
+                Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
+                // Display a warning toast that we failed to put a task on a secondary display.
                 mService.getTaskChangeNotificationController()
                         .notifyActivityLaunchOnSecondaryDisplayFailed();
                 return;
+            } else if (!forceNonResizable && handleForcedResizableTask(task,
+                    FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
+                return;
             }
         }
 
@@ -4554,16 +4553,23 @@
             return;
         }
 
+        handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
+    }
+
+    /**
+     * @return {@code true} if the top activity of the task is forced to be resizable and the user
+     *         was notified about activity being forced resized.
+     */
+    private boolean handleForcedResizableTask(TaskRecord task, int reason) {
         final ActivityRecord topActivity = task.getTopActivity();
         if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
-            && !topActivity.noDisplay) {
+                && !topActivity.noDisplay) {
             final String packageName = topActivity.appInfo.packageName;
-            final int reason = isSecondaryDisplayPreferred
-                    ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
-                    : FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
             mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
                     task.taskId, reason, packageName);
+            return true;
         }
+        return false;
     }
 
     void activityRelaunchedLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index eb41fe7..7df255d 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -52,6 +52,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -944,7 +945,8 @@
                 auxiliaryResponse == null ? null : auxiliaryResponse.filters);
     }
 
-    void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
+    void postStartActivityProcessing(ActivityRecord r, int result,
+            ActivityStack startedActivityStack) {
         if (ActivityManager.isStartResultFatalError(result)) {
             return;
         }
@@ -956,14 +958,6 @@
         // about this, so it waits for the new activity to become visible instead.
         mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
 
-        ActivityStack startedActivityStack = null;
-        final ActivityStack currentStack = r.getStack();
-        if (currentStack != null) {
-            startedActivityStack = currentStack;
-        } else if (mTargetStack != null) {
-            startedActivityStack = targetStack;
-        }
-
         if (startedActivityStack == null) {
             return;
         }
@@ -1239,23 +1233,41 @@
                 int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                 ActivityRecord[] outActivity) {
         int result = START_CANCELED;
+        final ActivityStack startedActivityStack;
         try {
             mService.mWindowManager.deferSurfaceLayout();
             result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                     startFlags, doResume, options, inTask, outActivity);
         } finally {
-            // If we are not able to proceed, disassociate the activity from the task. Leaving an
-            // activity in an incomplete state can lead to issues, such as performing operations
-            // without a window container.
-            final ActivityStack stack = mStartActivity.getStack();
-            if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
-                stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
-                        null /* intentResultData */, "startActivity", true /* oomAdj */);
+            final ActivityStack currentStack = r.getStack();
+            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+            if (ActivityManager.isStartResultSuccessful(result)) {
+                if (startedActivityStack != null) {
+                    // If there is no state change (e.g. a resumed activity is reparented to
+                    // top of another display) to trigger a visibility/configuration checking,
+                    // we have to update the configuration for changing to different display.
+                    final ActivityRecord currentTop =
+                            startedActivityStack.topRunningActivityLocked();
+                    if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+                        mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+                                true /* markFrozenIfConfigChanged */, false /* deferResume */);
+                    }
+                }
+            } else {
+                // If we are not able to proceed, disassociate the activity from the task.
+                // Leaving an activity in an incomplete state can lead to issues, such as
+                // performing operations without a window container.
+                final ActivityStack stack = mStartActivity.getStack();
+                if (stack != null) {
+                    stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+                            null /* intentResultData */, "startActivity", true /* oomAdj */);
+                }
             }
             mService.mWindowManager.continueSurfaceLayout();
         }
 
-        postStartActivityProcessing(r, result, mTargetStack);
+        postStartActivityProcessing(r, result, startedActivityStack);
 
         return result;
     }
@@ -1901,14 +1913,20 @@
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
         // the same behavior as if a new instance was being started, which means not bringing it
         // to the front if the caller is not itself in the front.
-        final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack();
-        ActivityRecord curTop = (focusStack == null)
-                ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+        final boolean differentTopTask;
+        if (mPreferredDisplayId == mTargetStack.mDisplayId) {
+            final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
+            final ActivityRecord curTop = (focusStack == null)
+                    ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+            final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+            differentTopTask = topTask != null
+                    && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+        } else {
+            // The existing task should always be different from those in other displays.
+            differentTopTask = true;
+        }
 
-        final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
-        if (topTask != null
-                && (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
-                && !mAvoidMoveToFront) {
+        if (differentTopTask && !mAvoidMoveToFront) {
             mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
             if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
                     mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 212844a..6935703d 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -4688,11 +4688,7 @@
         return mSleeping;
     }
 
-    /**
-     * Update AMS states when an activity is resumed. This should only be called by
-     * {@link ActivityStack#onActivityStateChanged(
-     * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
-     */
+    /** Update AMS states when an activity is resumed. */
     void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
         final TaskRecord task = r.getTask();
         if (task.isActivityTypeStandard()) {
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index f9dccea0..aad890b 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -22,6 +22,7 @@
 
 import android.annotation.Nullable;
 import android.os.FileUtils;
+import android.os.SystemProperties;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +39,10 @@
 final class MemoryStatUtil {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
 
+    /** True if device has per-app memcg */
+    private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
+            SystemProperties.getBoolean("ro.config.per_app_memcg", false);
+
     /** Path to check if device has memcg */
     private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
     /** Path to memory stat file for logging app start memory state */
@@ -55,15 +60,12 @@
     private static final int PGMAJFAULT_INDEX = 11;
     private static final int RSS_IN_BYTES_INDEX = 23;
 
-    /** True if device has memcg */
-    private static volatile Boolean sDeviceHasMemCg;
-
     private MemoryStatUtil() {}
 
     /**
      * Reads memory stat for a process.
      *
-     * Reads from memcg if available on device, else fallback to procfs.
+     * Reads from per-app memcg if available on device, else fallback to procfs.
      * Returns null if no stats can be read.
      */
     @Nullable
@@ -156,15 +158,10 @@
     }
 
     /**
-     * Checks if memcg is available on device.
-     *
-     * Touches the filesystem to do the check.
+     * Returns whether per-app memcg is available on device.
      */
     static boolean hasMemcg() {
-        if (sDeviceHasMemCg == null) {
-            sDeviceHasMemCg = (new File(MEMCG_TEST_PATH)).exists();
-        }
-        return sDeviceHasMemCg;
+        return DEVICE_HAS_PER_APP_MEMCG;
     }
 
     static final class MemoryStat {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7256e23..9b42d65 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1521,14 +1521,14 @@
 
     /**
      * Check whether this task can be launched on the specified display.
+     *
      * @param displayId Target display id.
-     * @return {@code true} if either it is the default display or this activity is resizeable and
-     *         can be put a secondary screen.
+     * @return {@code true} if either it is the default display or this activity can be put on a
+     *         secondary display.
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
         return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
-                isResizeable(false /* checkSupportsPip */), -1 /* don't check PID */,
-                -1 /* don't check UID */, null /* activityInfo */);
+                -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 36e7cba..6b09f1b 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -39,12 +39,17 @@
 
     public abstract int handleFailedAttempt();
     public abstract void resetFailedAttempts();
+    public abstract String getErrorString(int error, int vendorCode);
+    public abstract String getAcquiredString(int acquireInfo, int vendorCode);
+    /**
+      * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
+      */
+    public abstract int getBiometricType();
 
     public static final int LOCKOUT_NONE = 0;
     public static final int LOCKOUT_TIMED = 1;
     public static final int LOCKOUT_PERMANENT = 2;
 
-    private final BiometricAuthenticator mAuthenticator;
     // Callback mechanism received from the client
     // (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
     private IBiometricPromptReceiver mDialogReceiverFromClient;
@@ -88,15 +93,13 @@
             BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
             boolean restricted, String owner, Bundle bundle,
-            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
-            BiometricAuthenticator authenticator) {
+            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
         super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
                 restricted, owner);
         mOpId = opId;
         mBundle = bundle;
         mDialogReceiverFromClient = dialogReceiver;
         mStatusBarService = statusBarService;
-        mAuthenticator = authenticator;
         mHandler = new Handler(Looper.getMainLooper());
     }
 
@@ -115,8 +118,7 @@
         if (mBundle != null) {
             try {
                 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
-                    mStatusBarService.onBiometricHelp(
-                            mAuthenticator.getAcquiredString(acquiredInfo, vendorCode));
+                    mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
                 }
                 return false; // acquisition continues
             } catch (RemoteException e) {
@@ -144,8 +146,7 @@
         }
         if (mBundle != null) {
             try {
-                mStatusBarService.onBiometricError(
-                        mAuthenticator.getErrorString(error, vendorCode));
+                mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
             } catch (RemoteException e) {
                 Slog.e(getLogTag(), "Remote exception when sending error", e);
             }
@@ -220,7 +221,7 @@
                     // Send the lockout message to the system dialog
                     if (mBundle != null) {
                         mStatusBarService.onBiometricError(
-                                mAuthenticator.getErrorString(errorCode, 0 /* vendorCode */));
+                                getErrorString(errorCode, 0 /* vendorCode */));
                         mHandler.postDelayed(() -> {
                             try {
                                 listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
@@ -268,7 +269,7 @@
             if (mBundle != null) {
                 try {
                     mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
-                            mAuthenticator.getType());
+                            getBiometricType());
                 } catch (RemoteException e) {
                     Slog.e(getLogTag(), "Unable to show biometric dialog", e);
                 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a181b61..73c4223 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -108,6 +108,8 @@
     private ClientMonitor mPendingClient;
     private PerformanceStats mPerformanceStats;
     protected int mCurrentUserId = UserHandle.USER_NULL;
+    // Tracks if the current authentication makes use of CryptoObjects.
+    protected boolean mIsCrypto;
     // Normal authentications are tracked by mPerformanceMap.
     protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
     // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
@@ -217,16 +219,16 @@
      */
     protected void notifyClientActiveCallbacks(boolean isActive) {}
 
-    protected class AuthenticationClientImpl extends AuthenticationClient {
+    protected abstract class AuthenticationClientImpl extends AuthenticationClient {
 
         public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
                 boolean restricted, String owner, Bundle bundle,
                 IBiometricPromptReceiver dialogReceiver,
-                IStatusBarService statusBarService, BiometricAuthenticator authenticator) {
+                IStatusBarService statusBarService) {
             super(context, getMetrics(), daemon, halDeviceId, token, listener,
                     targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
-                    statusBarService, authenticator);
+                    statusBarService);
         }
 
         @Override
@@ -715,6 +717,7 @@
                 pmap.put(mCurrentUserId, stats);
             }
             mPerformanceStats = stats;
+            mIsCrypto = (opId != 0);
 
             startAuthentication(client, opPackageName);
         });
@@ -847,7 +850,7 @@
         return mKeyguardPackage.equals(clientPackage);
     }
 
-    private int getLockoutMode() {
+    protected int getLockoutMode() {
         final int currentUser = ActivityManager.getCurrentUser();
         final int failedAttempts = mFailedAttempts.get(currentUser, 0);
         if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
@@ -1109,4 +1112,4 @@
             LockoutResetMonitor monitor) {
         mLockoutMonitors.remove(monitor);
     }
-}
\ No newline at end of file
+}
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 8afac97..660710e 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -49,6 +49,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.biometrics.BiometricService;
@@ -82,6 +83,33 @@
     private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
     private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
 
+    private final class FaceAuthClient extends AuthenticationClientImpl {
+        public FaceAuthClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int targetUserId, int groupId, long opId,
+                boolean restricted, String owner, Bundle bundle,
+                IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
+            super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+                    restricted,
+                    owner, bundle, dialogReceiver, statusBarService);
+        }
+
+        @Override
+        public String getErrorString(int error, int vendorCode) {
+            return FaceManager.getErrorString(getContext(), error, vendorCode);
+        }
+
+        @Override
+        public String getAcquiredString(int acquireInfo, int vendorCode) {
+            return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
+        }
+
+        @Override
+        public int getBiometricType() {
+            return BiometricAuthenticator.TYPE_FACE;
+        }
+    }
+
     /**
      * Receives the incoming binder calls from FaceManager.
      */
@@ -128,10 +156,10 @@
                 final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             final boolean restricted = isRestricted();
-            final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+            final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
-                    null /* bundle */, null /* dialogReceiver */, mStatusBarService, mFaceManager);
+                    null /* bundle */, null /* dialogReceiver */, mStatusBarService);
 
             authenticateInternal(client, opId, opPackageName);
         }
@@ -143,11 +171,11 @@
                 int callingUid, int callingPid, int callingUserId) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+            final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(receiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
-                    bundle, dialogReceiver, mStatusBarService, mFaceManager);
+                    bundle, dialogReceiver, mStatusBarService);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -338,8 +366,8 @@
              */
             if (mBiometricPromptServiceReceiver != null) {
                 mBiometricPromptServiceReceiver.onAcquired(deviceId,
-                        mFaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
-                        mFaceManager.getAcquiredString(acquiredInfo, vendorCode));
+                        FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
+                        FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
             }
         }
 
@@ -362,7 +390,7 @@
         public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
             if (mBiometricPromptServiceReceiver != null) {
                 mBiometricPromptServiceReceiver.onError(deviceId, error,
-                        mFaceManager.getErrorString(error, vendorCode));
+                        FaceManager.getErrorString(getContext(), error, vendorCode));
             }
         }
     }
@@ -447,8 +475,6 @@
     @GuardedBy("this")
     private IBiometricsFace mDaemon;
     private long mHalDeviceId;
-    // Use FaceManager to get strings, so BiometricPrompt interface is cleaner
-    private FaceManager mFaceManager;
 
     /**
      * Receives callbacks from the HAL.
@@ -589,7 +615,6 @@
         super.onStart();
         publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
         SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
-        mFaceManager = (FaceManager) getContext().getSystemService(Context.FACE_SERVICE);
     }
 
     @Override
@@ -862,4 +887,4 @@
         mPerformanceMap.clear();
         mCryptoPerformanceMap.clear();
     }
-}
\ No newline at end of file
+}
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 95fb9e3..9f4fff8 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -50,12 +50,15 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricService;
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.ClientMonitor;
@@ -102,6 +105,33 @@
         }
     }
 
+    private final class FingerprintAuthClient extends AuthenticationClientImpl {
+        public FingerprintAuthClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int targetUserId, int groupId, long opId,
+                boolean restricted, String owner, Bundle bundle,
+                IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
+            super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
+                    restricted,
+                    owner, bundle, dialogReceiver, statusBarService);
+        }
+
+        @Override
+        public String getErrorString(int error, int vendorCode) {
+            return FingerprintManager.getErrorString(getContext(), error, vendorCode);
+        }
+
+        @Override
+        public String getAcquiredString(int acquireInfo, int vendorCode) {
+            return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
+        }
+
+        @Override
+        public int getBiometricType() {
+            return BiometricAuthenticator.TYPE_FINGERPRINT;
+        }
+    }
+
     /**
      * Receives the incoming binder calls from FingerprintManager.
      */
@@ -149,10 +179,10 @@
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName) {
             final boolean restricted = isRestricted();
-            final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+            final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
-                    null /* dialogReceiver */, mStatusBarService, mFingerprintManager);
+                    null /* dialogReceiver */, mStatusBarService);
 
             authenticateInternal(client, opId, opPackageName);
         }
@@ -164,11 +194,11 @@
                 int callingUid, int callingPid, int callingUserId) {
             checkPermission(MANAGE_BIOMETRIC);
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+            final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(receiver),
                     mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
-                    dialogReceiver, mStatusBarService, mFingerprintManager);
+                    dialogReceiver, mStatusBarService);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -371,7 +401,8 @@
                 throws RemoteException {
             if (mBiometricPromptServiceReceiver != null) {
                 mBiometricPromptServiceReceiver.onAcquired(deviceId, acquiredInfo,
-                        mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+                        FingerprintManager.getAcquiredString(
+                            getContext(), acquiredInfo, vendorCode));
             }
         }
 
@@ -394,7 +425,7 @@
         public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
             if (mBiometricPromptServiceReceiver != null) {
                 mBiometricPromptServiceReceiver.onError(deviceId, error,
-                        mFingerprintManager.getErrorString(error, vendorCode));
+                        FingerprintManager.getErrorString(getContext(), error, vendorCode));
             }
         }
     }
@@ -567,8 +598,6 @@
     private long mHalDeviceId;
     private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
     private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
-    // Use FingerprintManager to get strings, so BiometricPrompt interface is cleaner.
-    private FingerprintManager mFingerprintManager;
 
     /**
      * Receives callbacks from the HAL.
@@ -590,6 +619,11 @@
         public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
             mHandler.post(() -> {
                 FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+                if (getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+                        && getCurrentClient() instanceof AuthenticationClient) {
+                    // Ignore enrollment acquisitions or acquisitions when we are locked out.
+                    StatsLog.write(StatsLog.FINGERPRINT_ACQUIRED, mCurrentUserId, mIsCrypto);
+                }
             });
         }
 
@@ -599,6 +633,22 @@
             mHandler.post(() -> {
                 Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
                 FingerprintService.super.handleAuthenticated(fp, token);
+                // Send authentication to statsd.
+                final boolean authenticated = fingerId != 0;
+                StatsLog.write(StatsLog.FINGERPRINT_AUTHENTICATED, mCurrentUserId, mIsCrypto,
+                        authenticated);
+                if (!authenticated) {
+                    // If we failed to authenticate because of a lockout, inform statsd.
+                    final int lockoutMode = getLockoutMode();
+                    if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+                        StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId,
+                                mIsCrypto, StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__LOCKOUT);
+                    } else if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+                        StatsLog.write(StatsLog.FINGERPRINT_ERROR_OCCURRED, mCurrentUserId,
+                                mIsCrypto,
+                                StatsLog.FINGERPRINT_ERROR_OCCURRED__ERROR__PERMANENT_LOCKOUT);
+                    }
+                }
             });
         }
 
@@ -717,8 +767,6 @@
         super.onStart();
         publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
         SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
-        mFingerprintManager = (FingerprintManager)
-                getContext().getSystemService(Context.FINGERPRINT_SERVICE);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index c028a43..873a8e3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -639,12 +639,20 @@
             }
         }
 
-        // Otherwise only focused applications can access the clipboard.
-        boolean uidFocused = mWm.isUidFocused(callingUid);
-        if (!uidFocused) {
-            Slog.e(TAG, "Denying clipboard access to " + callingPackage
-                    + ", application is not in focus.");
+        switch (op) {
+            case AppOpsManager.OP_READ_CLIPBOARD:
+                // Clipboard can only be read by applications with focus.
+                boolean uidFocused = mWm.isUidFocused(callingUid);
+                if (!uidFocused) {
+                    Slog.e(TAG, "Denying clipboard access to " + callingPackage
+                            + ", application is not in focus.");
+                }
+                return uidFocused;
+            case AppOpsManager.OP_WRITE_CLIPBOARD:
+                // Writing is allowed without focus.
+                return true;
+            default:
+                throw new IllegalArgumentException("Unknown clipboard appop " + op);
         }
-        return uidFocused;
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b2287ac..a043201 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2536,7 +2536,11 @@
                     // We need to check if this is the current client with
                     // focus in the window manager, to allow this call to
                     // be made before input is started in it.
-                    if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+                    final ClientState cs = mClients.get(client.asBinder());
+                    if (cs == null) {
+                        throw new IllegalArgumentException("unknown client " + client.asBinder());
+                    }
+                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
                         Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
                         return false;
                     }
@@ -2616,7 +2620,11 @@
                     // We need to check if this is the current client with
                     // focus in the window manager, to allow this call to
                     // be made before input is started in it.
-                    if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+                    final ClientState cs = mClients.get(client.asBinder());
+                    if (cs == null) {
+                        throw new IllegalArgumentException("unknown client " + client.asBinder());
+                    }
+                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
                         if (DEBUG) {
                             Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
                         }
@@ -2734,7 +2742,7 @@
                             + client.asBinder());
                 }
 
-                if (!mWindowManagerInternal.inputMethodClientHasFocus(cs.client)) {
+                if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
                     // Check with the window manager to make sure this client actually
                     // has a window with focus.  If not, reject.  This is thread safe
                     // because if the focus changes some time before or after, the
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 050a075..d326c22 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -20,6 +20,7 @@
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.os.UserHandle.USER_ALL;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -53,12 +54,15 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
@@ -72,6 +76,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
 
@@ -138,10 +143,6 @@
     // not mean that we are currently bound to said package/component.
     private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
 
-    // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
-    // user change).
-    private int[] mLastSeenProfileIds;
-
     // True if approved services are stored in xml, not settings.
     private boolean mUseXml;
 
@@ -300,7 +301,7 @@
                 Settings.Secure.putStringForUser(
                         mContext.getContentResolver(), element, value, userId);
                 loadAllowedComponentsFromSettings();
-                rebindServices(false);
+                rebindServices(false, userId);
             }
         }
     }
@@ -385,7 +386,7 @@
                 }
             }
         }
-        rebindServices(false);
+        rebindServices(false, USER_ALL);
     }
 
     protected void upgradeXml(final int xmlVersion, final int userId) {}
@@ -460,7 +461,7 @@
             }
         }
 
-        rebindServices(false);
+        rebindServices(false, userId);
     }
 
     private String getApprovedValue(String pkgOrComponent) {
@@ -578,7 +579,7 @@
 
             if (anyServicesInvolved) {
                 // make sure we're still bound to any of our services who may have just upgraded
-                rebindServices(false);
+                rebindServices(false, USER_ALL);
             }
         }
     }
@@ -586,21 +587,17 @@
     public void onUserRemoved(int user) {
         Slog.i(TAG, "Removing approved services for removed user " + user);
         mApproved.remove(user);
-        rebindServices(true);
+        rebindServices(true, user);
     }
 
     public void onUserSwitched(int user) {
         if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
-        if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
-            if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
-            return;
-        }
-        rebindServices(true);
+        rebindServices(true, user);
     }
 
     public void onUserUnlocked(int user) {
         if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
-        rebindServices(false);
+        rebindServices(false, user);
     }
 
     private ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
@@ -694,9 +691,10 @@
                 component.flattenToShortString());
 
         synchronized (mMutex) {
-            final int[] userIds = mUserProfiles.getCurrentProfileIds();
+            final IntArray userIds = mUserProfiles.getCurrentProfileIds();
 
-            for (int userId : userIds) {
+            for (int i = 0; i < userIds.size(); i++) {
+                final int userId = userIds.get(i);
                 if (enabled) {
                     if (isPackageOrComponentAllowed(component.toString(), userId)
                             || isPackageOrComponentAllowed(component.getPackageName(), userId)) {
@@ -838,20 +836,14 @@
         return false;
     }
 
-    /**
-     * Called whenever packages change, the user switches, or the secure setting
-     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
-     */
-    protected void rebindServices(boolean forceRebind) {
-        if (DEBUG) Slog.d(TAG, "rebindServices");
-        final int[] userIds = mUserProfiles.getCurrentProfileIds();
-        final int nUserIds = userIds.length;
-
+    @VisibleForTesting
+    protected SparseArray<ArraySet<ComponentName>> getAllowedComponents(IntArray userIds) {
+        final int nUserIds = userIds.size();
         final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
 
         for (int i = 0; i < nUserIds; ++i) {
-            final int userId = userIds[i];
-            final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userIds[i]);
+            final int userId = userIds.get(i);
+            final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
             if (approvedLists != null) {
                 final int N = approvedLists.size();
                 for (int j = 0; j < N; j++) {
@@ -865,67 +857,125 @@
                 }
             }
         }
+        return componentsByUser;
+    }
 
-        final ArrayList<ManagedServiceInfo> removableBoundServices = new ArrayList<>();
-        final SparseArray<Set<ComponentName>> toAdd = new SparseArray<>();
-
-        synchronized (mMutex) {
-            // Rebind to non-system services if user switched
-            for (ManagedServiceInfo service : mServices) {
-                if (!service.isSystem && !service.isGuest(this)) {
-                    removableBoundServices.add(service);
-                }
-            }
-
-            mEnabledServicesForCurrentProfiles.clear();
-            mEnabledServicesPackageNames.clear();
-
-            for (int i = 0; i < nUserIds; ++i) {
-                // decode the list of components
-                final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
-                if (null == userComponents) {
-                    toAdd.put(userIds[i], new ArraySet<>());
-                    continue;
-                }
-
-                final Set<ComponentName> add = new HashSet<>(userComponents);
-                add.removeAll(mSnoozingForCurrentProfiles);
-
-                toAdd.put(userIds[i], add);
-
-                mEnabledServicesForCurrentProfiles.addAll(userComponents);
-
-                for (int j = 0; j < userComponents.size(); j++) {
-                    final ComponentName component = userComponents.valueAt(j);
-                    mEnabledServicesPackageNames.add(component.getPackageName());
-                }
-            }
-        }
-
-        for (ManagedServiceInfo info : removableBoundServices) {
-            final ComponentName component = info.component;
-            final int oldUser = info.userid;
-            final Set<ComponentName> allowedComponents = toAdd.get(info.userid);
-            if (allowedComponents != null) {
-                if (allowedComponents.contains(component) && !forceRebind) {
-                    // Already bound, don't need to bind again.
-                    allowedComponents.remove(component);
-                } else {
-                    // No longer allowed to be bound, or must rebind.
-                    Slog.v(TAG, "disabling " + getCaption() + " for user "
-                            + oldUser + ": " + component);
-                    unregisterService(component, oldUser);
-                }
-            }
-        }
+    @GuardedBy("mMutex")
+    protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
+            final IntArray activeUsers,
+            SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
+        mEnabledServicesForCurrentProfiles.clear();
+        mEnabledServicesPackageNames.clear();
+        final int nUserIds = activeUsers.size();
 
         for (int i = 0; i < nUserIds; ++i) {
-            final Set<ComponentName> add = toAdd.get(userIds[i]);
+            // decode the list of components
+            final int userId = activeUsers.get(i);
+            final ArraySet<ComponentName> userComponents = approvedComponentsByUser.get(userId);
+            if (null == userComponents) {
+                componentsToBind.put(userId, new ArraySet<>());
+                continue;
+            }
+
+            final Set<ComponentName> add = new HashSet<>(userComponents);
+            add.removeAll(mSnoozingForCurrentProfiles);
+
+            componentsToBind.put(userId, add);
+
+            mEnabledServicesForCurrentProfiles.addAll(userComponents);
+
+            for (int j = 0; j < userComponents.size(); j++) {
+                final ComponentName component = userComponents.valueAt(j);
+                mEnabledServicesPackageNames.add(component.getPackageName());
+            }
+        }
+    }
+
+    @GuardedBy("mMutex")
+    protected Set<ManagedServiceInfo> getRemovableConnectedServices() {
+        final Set<ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+        for (ManagedServiceInfo service : mServices) {
+            if (!service.isSystem && !service.isGuest(this)) {
+                removableBoundServices.add(service);
+            }
+        }
+        return removableBoundServices;
+    }
+
+    protected void populateComponentsToUnbind(
+            boolean forceRebind,
+            Set<ManagedServiceInfo> removableBoundServices,
+            SparseArray<Set<ComponentName>> allowedComponentsToBind,
+            SparseArray<Set<ComponentName>> componentsToUnbind) {
+        for (ManagedServiceInfo info : removableBoundServices) {
+            final Set<ComponentName> allowedComponents = allowedComponentsToBind.get(info.userid);
+            if (allowedComponents != null) {
+                if (forceRebind || !allowedComponents.contains(info.component)) {
+                    Set<ComponentName> toUnbind =
+                            componentsToUnbind.get(info.userid, new ArraySet<>());
+                    toUnbind.add(info.component);
+                    componentsToUnbind.put(info.userid, toUnbind);
+                }
+            }
+        }
+    }
+
+    /**
+     * Called whenever packages change, the user switches, or the secure setting
+     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+     */
+    protected void rebindServices(boolean forceRebind, int userToRebind) {
+        if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind);
+        IntArray userIds = mUserProfiles.getCurrentProfileIds();
+        if (userToRebind != USER_ALL) {
+            userIds = new IntArray(1);
+            userIds.add(userToRebind);
+        }
+
+        final SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+        final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        synchronized (mMutex) {
+            final SparseArray<ArraySet<ComponentName>> approvedComponentsByUser =
+                    getAllowedComponents(userIds);
+            final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+
+            // Filter approvedComponentsByUser to collect all of the components that are allowed
+            // for the currently active user(s).
+            populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+
+            // For every current non-system connection, disconnect services that are no longer
+            // approved, or ALL services if we are force rebinding
+            populateComponentsToUnbind(
+                    forceRebind, removableBoundServices, componentsToBind, componentsToUnbind);
+        }
+
+        unbindFromServices(componentsToUnbind);
+        bindToServices(componentsToBind);
+    }
+
+    protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
+        for (int i = 0; i < componentsToUnbind.size(); i++) {
+            final int userId = componentsToUnbind.keyAt(i);
+            final Set<ComponentName> removableComponents = componentsToUnbind.get(userId);
+            for (ComponentName cn : removableComponents) {
+                // No longer allowed to be bound, or must rebind.
+                Slog.v(TAG, "disabling " + getCaption() + " for user " + userId + ": " + cn);
+                unregisterService(cn, userId);
+            }
+        }
+    }
+
+    // Attempt to bind to services, skipping those that cannot be found or lack the permission.
+    private void bindToServices(SparseArray<Set<ComponentName>> componentsToBind) {
+        for (int i = 0; i < componentsToBind.size(); i++) {
+            final int userId = componentsToBind.keyAt(i);
+            final Set<ComponentName> add = componentsToBind.get(userId);
             for (ComponentName component : add) {
                 try {
                     ServiceInfo info = mPm.getServiceInfo(component,
                             PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
                     if (info == null) {
                         Slog.w(TAG, "Not binding " + getCaption() + " service " + component
                                 + ": service not found");
@@ -937,15 +987,13 @@
                         continue;
                     }
                     Slog.v(TAG,
-                            "enabling " + getCaption() + " for " + userIds[i] + ": " + component);
-                    registerService(component, userIds[i]);
+                            "enabling " + getCaption() + " for " + userId + ": " + component);
+                    registerService(component, userId);
                 } catch (RemoteException e) {
                     e.rethrowFromSystemServer();
                 }
             }
         }
-
-        mLastSeenProfileIds = userIds;
     }
 
     /**
@@ -1022,7 +1070,7 @@
 
                 @Override
                 public void onServiceConnected(ComponentName name, IBinder binder) {
-                    Slog.v(TAG, getCaption() + " service connected: " + name);
+                    Slog.v(TAG,  userid + " " + getCaption() + " service connected: " + name);
                     boolean added = false;
                     ManagedServiceInfo info = null;
                     synchronized (mMutex) {
@@ -1044,12 +1092,12 @@
 
                 @Override
                 public void onServiceDisconnected(ComponentName name) {
-                    Slog.v(TAG, getCaption() + " connection lost: " + name);
+                    Slog.v(TAG, userid + " " + getCaption() + " connection lost: " + name);
                 }
 
                 @Override
                 public void onBindingDied(ComponentName name) {
-                    Slog.w(TAG, getCaption() + " binding died: " + name);
+                    Slog.w(TAG,  userid + " " + getCaption() + " binding died: " + name);
                     synchronized (mMutex) {
                         unbindService(this, name, userid);
                         if (!mServicesRebinding.contains(servicesBindingTag)) {
@@ -1061,8 +1109,8 @@
                                     }
                                }, ON_BINDING_DIED_REBIND_DELAY_MS);
                         } else {
-                            Slog.v(TAG, getCaption() + " not rebinding as "
-                                    + "a previous rebind attempt was made: " + name);
+                            Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+                                    + " as a previous rebind attempt was made: " + name);
                         }
                     }
                 }
@@ -1072,7 +1120,8 @@
                     getBindFlags(),
                     new UserHandle(userid))) {
                 mServicesBound.remove(servicesBindingTag);
-                Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+                Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent
+                        + " in user " + userid);
                 return;
             }
         } catch (SecurityException ex) {
@@ -1236,9 +1285,9 @@
             if (!isEnabledForCurrentProfiles()) {
                 return false;
             }
-            if (this.userid == UserHandle.USER_ALL) return true;
+            if (this.userid == USER_ALL) return true;
             if (this.isSystem) return true;
-            if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+            if (nid == USER_ALL || nid == this.userid) return true;
             return supportsProfiles()
                     && mUserProfiles.isCurrentProfile(nid)
                     && isPermittedForProfile(nid);
@@ -1284,6 +1333,24 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ManagedServiceInfo that = (ManagedServiceInfo) o;
+            return userid == that.userid
+                    && isSystem == that.isSystem
+                    && targetSdkVersion == that.targetSdkVersion
+                    && Objects.equals(service, that.service)
+                    && Objects.equals(component, that.component)
+                    && Objects.equals(connection, that.connection);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(service, component, userid, isSystem, connection, targetSdkVersion);
+        }
     }
 
     /** convenience method for looking in mEnabledServicesForCurrentProfiles */
@@ -1309,12 +1376,15 @@
             }
         }
 
-        public int[] getCurrentProfileIds() {
+        /**
+         * Returns the currently active users (generally one user and its work profile).
+         */
+        public IntArray getCurrentProfileIds() {
             synchronized (mCurrentProfiles) {
-                int[] users = new int[mCurrentProfiles.size()];
+                IntArray users = new IntArray(mCurrentProfiles.size());
                 final int N = mCurrentProfiles.size();
                 for (int i = 0; i < N; ++i) {
-                    users[i] = mCurrentProfiles.keyAt(i);
+                    users.add(mCurrentProfiles.keyAt(i));
                 }
                 return users;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 33f0172..e53eeb0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -178,6 +178,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1566,7 +1567,7 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
-        getContext().registerReceiver(mIntentReceiver, filter);
+        getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
 
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -1698,10 +1699,10 @@
                     UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
                     null);
             if (isUidSystemOrPhone(uid)) {
-                int[] profileIds = mUserProfiles.getCurrentProfileIds();
-                int N = profileIds.length;
+                IntArray profileIds = mUserProfiles.getCurrentProfileIds();
+                int N = profileIds.size();
                 for (int i = 0; i < N; i++) {
-                    int profileId = profileIds[i];
+                    int profileId = profileIds.get(i);
                     cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
                             profileId, REASON_CHANNEL_BANNED,
                             null);
@@ -6691,7 +6692,9 @@
         @Override
         public void onUserUnlocked(int user) {
             if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
-            rebindServices(true);
+            // force rebind the assistant, as it might be keeping its own state in user locked
+            // storage
+            rebindServices(true, user);
         }
 
         protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index a178a52..2b581d6 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -15,17 +15,8 @@
  */
 package com.android.server.notification;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.NonNull;
 import android.app.AlarmManager;
-import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -37,9 +28,18 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -105,12 +105,12 @@
 
     protected @NonNull List<NotificationRecord> getSnoozed() {
         List<NotificationRecord> snoozedForUser = new ArrayList<>();
-        int[] userIds = mUserProfiles.getCurrentProfileIds();
+        IntArray userIds = mUserProfiles.getCurrentProfileIds();
         if (userIds != null) {
-            final int N = userIds.length;
+            final int N = userIds.size();
             for (int i = 0; i < N; i++) {
                 final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
-                        mSnoozedNotifications.get(userIds[i]);
+                        mSnoozedNotifications.get(userIds.get(i));
                 if (snoozedPkgs != null) {
                     final int M = snoozedPkgs.size();
                     for (int j = 0; j < M; j++) {
@@ -179,7 +179,7 @@
     protected boolean cancel(int userId, boolean includeCurrentProfiles) {
         int[] userIds = {userId};
         if (includeCurrentProfiles) {
-            userIds = mUserProfiles.getCurrentProfileIds();
+            userIds = mUserProfiles.getCurrentProfileIds().toArray();
         }
         final int N = userIds.length;
         for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 5bf323a..3b11525 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1049,7 +1049,7 @@
                                 (other != null && other.getComponentName() != null)
                                         ? other.getComponentName().getPackageName() : "?";
                         // if we're installing over the same already-installed package, this is ok
-                        if (otherPackageName != pkg.packageName) {
+                        if (!otherPackageName.equals(pkg.packageName)) {
                             throw new PackageManagerException(
                                     INSTALL_FAILED_CONFLICTING_PROVIDER,
                                     "Can't install because provider name " + names[j]
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index bca3ca7..5810e30 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -53,16 +53,13 @@
     private final static String TAG = "OTADexopt";
     private final static boolean DEBUG_DEXOPT = true;
 
-    // The synthetic library dependencies denoting "no checks."
-    private final static String[] NO_LIBRARIES =
-            new String[] { PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK };
-
     // The amount of "available" (free - low threshold) space necessary at the start of an OTA to
     // not bulk-delete unused apps' odex files.
     private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024;  // 1GB.
 
     private final Context mContext;
     private final PackageManagerService mPackageManagerService;
+    private final MetricsLogger metricsLogger;
 
     // TODO: Evaluate the need for WeakReferences here.
 
@@ -95,6 +92,7 @@
     public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
         this.mContext = context;
         this.mPackageManagerService = packageManagerService;
+        metricsLogger = new MetricsLogger();
     }
 
     public static OtaDexoptService main(Context context,
@@ -286,8 +284,8 @@
                     throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The current version.
-                builder.append("9 ");
+                // The current version. For v10, see b/115993344.
+                builder.append("10 ");
 
                 builder.append("dexopt");
 
@@ -336,11 +334,6 @@
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
 
         String[] libraryDependencies = pkg.usesLibraryFiles;
-        if (pkg.isSystem()) {
-            // For system apps, we want to avoid classpaths checks.
-            libraryDependencies = NO_LIBRARIES;
-        }
-
 
         optimizer.performDexOpt(pkg, libraryDependencies,
                 null /* ISAs */,
@@ -445,24 +438,22 @@
     private void performMetricsLogging() {
         long finalTime = System.nanoTime();
 
-        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
+        metricsLogger.histogram("ota_dexopt_available_space_before_mb",
                 inMegabytes(availableSpaceBefore));
-        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
+        metricsLogger.histogram("ota_dexopt_available_space_after_bulk_delete_mb",
                 inMegabytes(availableSpaceAfterBulkDelete));
-        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
+        metricsLogger.histogram("ota_dexopt_available_space_after_dexopt_mb",
                 inMegabytes(availableSpaceAfterDexopt));
 
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
-                importantPackageCount);
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
+        metricsLogger.histogram("ota_dexopt_num_important_packages", importantPackageCount);
+        metricsLogger.histogram("ota_dexopt_num_other_packages", otherPackageCount);
 
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
-                dexoptCommandCountExecuted);
+        metricsLogger.histogram("ota_dexopt_num_commands", dexoptCommandCountTotal);
+        metricsLogger.histogram("ota_dexopt_num_commands_executed", dexoptCommandCountExecuted);
 
         final int elapsedTimeSeconds =
                 (int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
-        MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
+        metricsLogger.histogram("ota_dexopt_time_s", elapsedTimeSeconds);
     }
 
     private static class OTADexoptPackageDexOptimizer extends
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 d9eb7e8..3c9dd63 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -946,7 +946,7 @@
     }
 
     private boolean isSystemPackage(String packageName) {
-        return isSystemPackage(getSystemPackageInfo(packageName));
+        return isSystemPackage(getPackageInfo(packageName));
     }
 
     private boolean isSystemPackage(PackageInfo pkg) {
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
new file mode 100644
index 0000000..e212b04
--- /dev/null
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -0,0 +1,50 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "WmTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        }
+      ]
+    },
+    {
+      "name": "WmTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5981ab0..68e636a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,6 +49,7 @@
 import android.util.Slog;
 import android.util.StatsLog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -75,7 +76,8 @@
  * tell the system when we go to sleep so that it can lock the keyguard if needed.
  * </p>
  */
-final class Notifier {
+@VisibleForTesting
+public class Notifier {
     private static final String TAG = "PowerManagerNotifier";
 
     private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bdf12ca..9f6b3dd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -64,7 +64,6 @@
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
 import android.service.vr.IVrManager;
@@ -84,7 +83,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LockGuard;
@@ -230,6 +228,10 @@
     private final BatterySaverController mBatterySaverController;
     private final BatterySaverStateMachine mBatterySaverStateMachine;
     private final BatterySavingStats mBatterySavingStats;
+    private final BinderService mBinderService;
+    private final LocalService mLocalService;
+    private final NativeWrapper mNativeWrapper;
+    private final Injector mInjector;
 
     private LightsManager mLightsManager;
     private BatteryManagerInternal mBatteryManagerInternal;
@@ -636,10 +638,76 @@
         }
     }
 
+    /**
+     * Wrapper around the static-native methods of PowerManagerService.
+     *
+     * This class exists to allow us to mock static native methods in our tests. If mocking static
+     * methods becomes easier than this in the future, we can delete this class.
+     */
+    @VisibleForTesting
+    public static class NativeWrapper {
+        /** Wrapper for PowerManager.nativeInit */
+        public void nativeInit(PowerManagerService service) {
+            service.nativeInit();
+        }
+
+        /** Wrapper for PowerManager.nativeAcquireSuspectBlocker */
+        public void nativeAcquireSuspendBlocker(String name) {
+            PowerManagerService.nativeAcquireSuspendBlocker(name);
+        }
+
+        /** Wrapper for PowerManager.nativeReleaseSuspendBlocker */
+        public void nativeReleaseSuspendBlocker(String name) {
+            PowerManagerService.nativeReleaseSuspendBlocker(name);
+        }
+
+        /** Wrapper for PowerManager.nativeSetInteractive */
+        public void nativeSetInteractive(boolean enable) {
+            PowerManagerService.nativeSetInteractive(enable);
+        }
+
+        /** Wrapper for PowerManager.nativeSetAutoSuspend */
+        public void nativeSetAutoSuspend(boolean enable) {
+            PowerManagerService.nativeSetAutoSuspend(enable);
+        }
+
+        /** Wrapper for PowerManager.nativeSendPowerHint */
+        public void nativeSendPowerHint(int hintId, int data) {
+            PowerManagerService.nativeSendPowerHint(hintId, data);
+        }
+
+        /** Wrapper for PowerManager.nativeSetFeature */
+        public void nativeSetFeature(int featureId, int data) {
+            PowerManagerService.nativeSetFeature(featureId, data);
+        }
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+            return new Notifier(looper, context, batteryStats, suspendBlocker, policy);
+        }
+
+        SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+            SuspendBlocker suspendBlocker = service.new SuspendBlockerImpl(name);
+            service.mSuspendBlockers.add(suspendBlocker);
+            return suspendBlocker;
+        }
+
+        BatterySaverPolicy createBatterySaverPolicy(
+                Object lock, Context context, BatterySavingStats batterySavingStats) {
+            return new BatterySaverPolicy(lock, context, batterySavingStats);
+        }
+
+        NativeWrapper createNativeWrapper() {
+            return new NativeWrapper();
+        }
+    }
+
     final Constants mConstants;
 
     private native void nativeInit();
-
     private static native void nativeAcquireSuspendBlocker(String name);
     private static native void nativeReleaseSuspendBlocker(String name);
     private static native void nativeSetInteractive(boolean enable);
@@ -648,8 +716,19 @@
     private static native void nativeSetFeature(int featureId, int data);
 
     public PowerManagerService(Context context) {
+        this(context, new Injector());
+    }
+
+    @VisibleForTesting
+    PowerManagerService(Context context, Injector injector) {
         super(context);
+
         mContext = context;
+        mBinderService = new BinderService();
+        mLocalService = new LocalService();
+        mNativeWrapper = injector.createNativeWrapper();
+        mInjector = injector;
+
         mHandlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
         mHandlerThread.start();
@@ -658,57 +737,40 @@
         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
 
         mBatterySavingStats = new BatterySavingStats(mLock);
-        mBatterySaverPolicy = new BatterySaverPolicy(mLock, mContext, mBatterySavingStats);
+        mBatterySaverPolicy =
+                mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
         mBatterySaverController = new BatterySaverController(mLock, mContext,
-                BackgroundThread.get().getLooper(), mBatterySaverPolicy, mBatterySavingStats);
+                BackgroundThread.get().getLooper(), mBatterySaverPolicy,
+                mBatterySavingStats);
         mBatterySaverStateMachine = new BatterySaverStateMachine(
                 mLock, mContext, mBatterySaverController);
 
         synchronized (mLock) {
-            mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
-            mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
-            mDisplaySuspendBlocker.acquire();
-            mHoldingDisplaySuspendBlocker = true;
+            mWakeLockSuspendBlocker =
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
+            mDisplaySuspendBlocker =
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+            if (mDisplaySuspendBlocker != null) {
+                mDisplaySuspendBlocker.acquire();
+                mHoldingDisplaySuspendBlocker = true;
+            }
             mHalAutoSuspendModeEnabled = false;
             mHalInteractiveModeEnabled = true;
 
             mWakefulness = WAKEFULNESS_AWAKE;
-
             sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
 
-            nativeInit();
-            nativeSetAutoSuspend(false);
-            nativeSetInteractive(true);
-            nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
+            mNativeWrapper.nativeInit(this);
+            mNativeWrapper.nativeSetAutoSuspend(false);
+            mNativeWrapper.nativeSetInteractive(true);
+            mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
         }
     }
 
-    @VisibleForTesting
-    PowerManagerService(Context context, BatterySaverPolicy batterySaverPolicy) {
-        super(context);
-
-        mContext = context;
-        mHandlerThread = new ServiceThread(TAG,
-                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
-        mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
-        mConstants = new Constants(mHandler);
-        mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
-        mDisplaySuspendBlocker = null;
-        mWakeLockSuspendBlocker = null;
-
-        mBatterySavingStats = new BatterySavingStats(mLock);
-        mBatterySaverPolicy = batterySaverPolicy;
-        mBatterySaverController = new BatterySaverController(mLock, context,
-                BackgroundThread.getHandler().getLooper(), batterySaverPolicy, mBatterySavingStats);
-        mBatterySaverStateMachine = new BatterySaverStateMachine(
-                mLock, mContext, mBatterySaverController);
-    }
-
     @Override
     public void onStart() {
-        publishBinderService(Context.POWER_SERVICE, new BinderService());
-        publishLocalService(PowerManagerInternal.class, new LocalService());
+        publishBinderService(Context.POWER_SERVICE, mBinderService);
+        publishLocalService(PowerManagerInternal.class, mLocalService);
 
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
@@ -752,11 +814,13 @@
             // The notifier runs on the system server's main looper so as not to interfere
             // with the animations and other critical functions of the power manager.
             mBatteryStats = BatteryStatsService.getService();
-            mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
-                    createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
+            mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
+                    mPolicy);
 
             mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
-                    createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
+                    mInjector.createSuspendBlocker(
+                            this, "PowerManagerService.WirelessChargerDetector"),
                     mHandler);
             mSettingsObserver = new SettingsObserver(mHandler);
 
@@ -824,7 +888,7 @@
         resolver.registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.DEVICE_DEMO_MODE),
                 false, mSettingsObserver, UserHandle.USER_SYSTEM);
-        IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+        IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE));
         if (vrManager != null) {
             try {
                 vrManager.registerListener(mVrStateCallbacks);
@@ -927,7 +991,8 @@
                             UserHandle.USER_CURRENT) != 0;
             if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
                 mDoubleTapWakeEnabled = doubleTapWakeEnabled;
-                nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+                mNativeWrapper.nativeSetFeature(
+                        POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
             }
         }
 
@@ -1509,6 +1574,11 @@
         }
     }
 
+    @VisibleForTesting
+    int getWakefulness() {
+        return mWakefulness;
+    }
+
     /**
      * Logs the time the device would have spent awake before user activity timeout,
      * had the system not been told the user was inactive.
@@ -2640,7 +2710,7 @@
             mHalAutoSuspendModeEnabled = enable;
             Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
             try {
-                nativeSetAutoSuspend(enable);
+                mNativeWrapper.nativeSetAutoSuspend(enable);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_POWER);
             }
@@ -2655,7 +2725,7 @@
             mHalInteractiveModeEnabled = enable;
             Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
             try {
-                nativeSetInteractive(enable);
+                mNativeWrapper.nativeSetInteractive(enable);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_POWER);
             }
@@ -3127,7 +3197,7 @@
                 break;
         }
 
-        nativeSendPowerHint(hintId, data);
+        mNativeWrapper.nativeSendPowerHint(hintId, data);
     }
 
     /**
@@ -3718,12 +3788,6 @@
         proto.flush();
     }
 
-    private SuspendBlocker createSuspendBlockerLocked(String name) {
-        SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
-        mSuspendBlockers.add(suspendBlocker);
-        return suspendBlocker;
-    }
-
     private void incrementBootCount() {
         synchronized (mLock) {
             int count;
@@ -4022,7 +4086,7 @@
                     Slog.wtf(TAG, "Suspend blocker \"" + mName
                             + "\" was finalized without being released!");
                     mReferenceCount = 0;
-                    nativeReleaseSuspendBlocker(mName);
+                    mNativeWrapper.nativeReleaseSuspendBlocker(mName);
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                 }
             } finally {
@@ -4039,7 +4103,7 @@
                         Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
                     }
                     Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
-                    nativeAcquireSuspendBlocker(mName);
+                    mNativeWrapper.nativeAcquireSuspendBlocker(mName);
                 }
             }
         }
@@ -4052,7 +4116,7 @@
                     if (DEBUG_SPEW) {
                         Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
                     }
-                    nativeReleaseSuspendBlocker(mName);
+                    mNativeWrapper.nativeReleaseSuspendBlocker(mName);
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                 } else if (mReferenceCount < 0) {
                     Slog.wtf(TAG, "Suspend blocker \"" + mName
@@ -4090,7 +4154,8 @@
         }
     }
 
-    private final class BinderService extends IPowerManager.Stub {
+    @VisibleForTesting
+    final class BinderService extends IPowerManager.Stub {
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
@@ -4592,6 +4657,16 @@
     }
 
     @VisibleForTesting
+    BinderService getBinderServiceInstance() {
+        return mBinderService;
+    }
+
+    @VisibleForTesting
+    LocalService getLocalServiceInstance() {
+        return mLocalService;
+    }
+
+    @VisibleForTesting
     // lastRebootReasonProperty argument to permit testing
     int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
         String line = SystemProperties.get(lastRebootReasonProperty);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index a4d42a1..5e3fe0a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -32,6 +32,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.hardware.fingerprint.FingerprintManager;
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -122,7 +123,7 @@
     public static final String CONFIG_DIR = "/data/misc/stats-service";
 
     static final String TAG = "StatsCompanionService";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = true;
 
     public static final int CODE_DATA_BROADCAST = 1;
     public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -253,34 +254,38 @@
 
     @Override
     public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
-                                        long subscriptionId, long subscriptionRuleId,
-                                        String[] cookies,
-                                        StatsDimensionsValue dimensionsValue) {
+            long subscriptionId, long subscriptionRuleId, String[] cookies,
+            StatsDimensionsValue dimensionsValue) {
         enforceCallingPermission();
         IntentSender intentSender = new IntentSender(intentSenderBinder);
-        Intent intent = new Intent()
-                .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
-                .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
-                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
-                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
-                .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+        Intent intent =
+                new Intent()
+                        .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+                        .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
+                        .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+                        .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
+                        .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
 
         ArrayList<String> cookieList = new ArrayList<>(cookies.length);
-        for (String cookie : cookies) { cookieList.add(cookie); }
+        for (String cookie : cookies) {
+            cookieList.add(cookie);
+        }
         intent.putStringArrayListExtra(
                 StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
 
         if (DEBUG) {
-            Slog.d(TAG, String.format(
-                    "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
-                    configUid, configKey, subscriptionId, subscriptionRuleId,
-                    Arrays.toString(cookies), dimensionsValue));
+            Slog.d(TAG,
+                    String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+                            configUid, configKey, subscriptionId, subscriptionRuleId,
+                            Arrays.toString(cookies),
+                            dimensionsValue));
         }
         try {
             intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
         } catch (IntentSender.SendIntentException e) {
-            Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
-                    + "; presumably it had been cancelled.");
+            Slog.w(TAG,
+                    "Unable to send using IntentSender from uid " + configUid
+                            + "; presumably it had been cancelled.");
         }
     }
 
@@ -317,7 +322,7 @@
         // Add in all the apps for every user/profile.
         for (UserInfo profile : users) {
             List<PackageInfo> pi =
-                pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
+                    pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
             for (int j = 0; j < pi.size(); j++) {
                 if (pi.get(j).applicationInfo != null) {
                     uids.add(pi.get(j).applicationInfo.uid);
@@ -402,8 +407,9 @@
     public final static class PullingAlarmListener implements OnAlarmListener {
         @Override
         public void onAlarm() {
-            if (DEBUG)
+            if (DEBUG) {
                 Slog.d(TAG, "Time to poll something.");
+            }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
                     Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
@@ -422,8 +428,9 @@
     public final static class PeriodicAlarmListener implements OnAlarmListener {
         @Override
         public void onAlarm() {
-            if (DEBUG)
+            if (DEBUG) {
                 Slog.d(TAG, "Time to trigger periodic alarm.");
+            }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
                     Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
@@ -459,7 +466,7 @@
                     return;
                 }
                 try {
-                  sStatsd.informDeviceShutdown();
+                    sStatsd.informDeviceShutdown();
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
                 }
@@ -498,9 +505,11 @@
     @Override // Binder call
     public void setAlarmForSubscriberTriggering(long timestampMs) {
         enforceCallingPermission();
-        if (DEBUG)
-            Slog.d(TAG, "Setting periodic alarm in about " +
-                    (timestampMs - SystemClock.elapsedRealtime()));
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "Setting periodic alarm in about " + (timestampMs
+                            - SystemClock.elapsedRealtime()));
+        }
         final long callingToken = Binder.clearCallingIdentity();
         try {
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
@@ -515,8 +524,9 @@
     @Override // Binder call
     public void cancelAlarmForSubscriberTriggering() {
         enforceCallingPermission();
-        if (DEBUG)
+        if (DEBUG) {
             Slog.d(TAG, "Cancelling periodic alarm");
+        }
         final long callingToken = Binder.clearCallingIdentity();
         try {
             mAlarmManager.cancel(mPeriodicAlarmListener);
@@ -546,8 +556,9 @@
     @Override // Binder call
     public void cancelPullingAlarm() {
         enforceCallingPermission();
-        if (DEBUG)
+        if (DEBUG) {
             Slog.d(TAG, "Cancelling pulling alarm");
+        }
         final long callingToken = Binder.clearCallingIdentity();
         try {
             mAlarmManager.cancel(mPullingAlarmListener);
@@ -560,10 +571,11 @@
             int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
         int size = stats.size();
         long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
         NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
         for (int j = 0; j < size; j++) {
             stats.getValues(j, entry);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
             e.writeInt(entry.uid);
             if (withFGBG) {
                 e.writeInt(entry.set);
@@ -639,14 +651,15 @@
         return null;
     }
 
-    private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullKernelWakelock(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         final KernelWakelockStats wakelockStats =
                 mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
             String name = ent.getKey();
             KernelWakelockStats.Entry kws = ent.getValue();
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeString(name);
             e.writeInt(kws.mCount);
             e.writeInt(kws.mVersion);
@@ -655,7 +668,9 @@
         }
     }
 
-    private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiBytesTransfer(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             // TODO: Consider caching the following call to get BatteryStatsInternal.
@@ -667,7 +682,8 @@
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             // Combine all the metrics per Uid into one record.
             NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null)
                             .groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
         } catch (java.io.IOException e) {
@@ -677,7 +693,9 @@
         }
     }
 
-    private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiBytesTransferByFgBg(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -687,7 +705,8 @@
             }
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null));
             addNetworkStats(tagId, pulledData, stats, true);
         } catch (java.io.IOException e) {
             Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
@@ -696,7 +715,9 @@
         }
     }
 
-    private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullMobileBytesTransfer(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -707,7 +728,8 @@
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             // Combine all the metrics per Uid into one record.
             NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null)
                             .groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
         } catch (java.io.IOException e) {
@@ -717,12 +739,14 @@
         }
     }
 
-    private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBluetoothBytesTransfer(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BluetoothActivityEnergyInfo info = pullBluetoothData();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         if (info.getUidTraffic() != null) {
             for (UidTraffic traffic : info.getUidTraffic()) {
-                StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
                 e.writeInt(traffic.getUid());
                 e.writeLong(traffic.getRxBytes());
                 e.writeLong(traffic.getTxBytes());
@@ -731,7 +755,9 @@
         }
     }
 
-    private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullMobileBytesTransferByFgBg(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -741,7 +767,8 @@
             }
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null));
             addNetworkStats(tagId, pulledData, stats, true);
         } catch (java.io.IOException e) {
             Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
@@ -750,13 +777,15 @@
         }
     }
 
-    private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullCpuTimePerFreq(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
             long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
             if (clusterTimeMs != null) {
                 for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                            wallClockNanos);
                     e.writeInt(cluster);
                     e.writeInt(speed);
                     e.writeLong(clusterTimeMs[speed]);
@@ -766,10 +795,11 @@
         }
     }
 
-    private void pullKernelUidCpuTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuTimeReader.readAbsolute((uid, userTimeUs, systemTimeUs) -> {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(uid);
             e.writeLong(userTimeUs);
             e.writeLong(systemTimeUs);
@@ -777,12 +807,14 @@
         });
     }
 
-    private void pullKernelUidCpuFreqTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuFreqTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
             for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
-                if(cpuFreqTimeMs[freqIndex] != 0) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                if (cpuFreqTimeMs[freqIndex] != 0) {
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                            wallClockNanos);
                     e.writeInt(uid);
                     e.writeInt(freqIndex);
                     e.writeLong(cpuFreqTimeMs[freqIndex]);
@@ -792,11 +824,13 @@
         });
     }
 
-    private void pullKernelUidCpuClusterTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuClusterTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
             for (int i = 0; i < cpuClusterTimesMs.length; i++) {
-                StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
                 e.writeInt(uid);
                 e.writeInt(i);
                 e.writeLong(cpuClusterTimesMs[i]);
@@ -805,17 +839,20 @@
         });
     }
 
-    private void pullKernelUidCpuActiveTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuActiveTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(uid);
-            e.writeLong((long)cpuActiveTimesMs);
+            e.writeLong((long) cpuActiveTimesMs);
             pulledData.add(e);
         });
     }
 
-    private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiActivityInfo(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         if (mWifiManager == null) {
             mWifiManager =
@@ -826,7 +863,8 @@
                 SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
                 mWifiManager.requestActivityInfo(wifiReceiver);
                 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
-                StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
                 e.writeLong(wifiInfo.getTimeStamp());
                 e.writeInt(wifiInfo.getStackState());
                 e.writeLong(wifiInfo.getControllerTxTimeMillis());
@@ -835,14 +873,18 @@
                 e.writeLong(wifiInfo.getControllerEnergyUsed());
                 pulledData.add(e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+                Slog.e(TAG,
+                        "Pulling wifiManager for wifi controller activity energy info has error",
+                        e);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
     }
 
-    private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullModemActivityInfo(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         if (mTelephony == null) {
             mTelephony = TelephonyManager.from(mContext);
@@ -851,7 +893,7 @@
             SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
             mTelephony.requestModemActivityInfo(modemReceiver);
             final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 10);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeLong(modemInfo.getTimestamp());
             e.writeLong(modemInfo.getSleepTimeMillis());
             e.writeLong(modemInfo.getIdleTimeMillis());
@@ -866,9 +908,11 @@
         }
     }
 
-    private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBluetoothActivityInfo(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BluetoothActivityEnergyInfo info = pullBluetoothData();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(info.getTimeStamp());
         e.writeInt(info.getBluetoothStackState());
         e.writeLong(info.getControllerTxTimeMillis());
@@ -881,7 +925,8 @@
     private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null) {
-            SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
+            SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
+                    "bluetooth");
             adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
             return awaitControllerInfo(bluetoothReceiver);
         } else {
@@ -890,25 +935,29 @@
         }
     }
 
-    private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+    private void pullSystemElapsedRealtime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(SystemClock.elapsedRealtime());
         pulledData.add(e);
     }
 
-    private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+    private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(SystemClock.uptimeMillis());
         pulledData.add(e);
     }
 
-    private void pullProcessMemoryState(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullProcessMemoryState(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         List<ProcessMemoryState> processMemoryStates =
-                LocalServices.getService(ActivityManagerInternal.class)
-                        .getMemoryStateForProcesses();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+                LocalServices.getService(
+                        ActivityManagerInternal.class).getMemoryStateForProcesses();
         for (ProcessMemoryState processMemoryState : processMemoryStates) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(processMemoryState.uid);
             e.writeString(processMemoryState.processName);
             e.writeInt(processMemoryState.oomScore);
@@ -921,7 +970,9 @@
         }
     }
 
-    private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBinderCallsStats(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BinderCallsStatsService.Internal binderStats =
                 LocalServices.getService(BinderCallsStatsService.Internal.class);
         if (binderStats == null) {
@@ -930,9 +981,8 @@
 
         List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
         binderStats.reset();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (ExportedCallStat callStat : callStats) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 13 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(callStat.uid);
             e.writeString(callStat.className);
             e.writeString(callStat.methodName);
@@ -950,7 +1000,9 @@
         }
     }
 
-    private void pullBinderCallsStatsExceptions(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBinderCallsStatsExceptions(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BinderCallsStatsService.Internal binderStats =
                 LocalServices.getService(BinderCallsStatsService.Internal.class);
         if (binderStats == null) {
@@ -960,16 +1012,16 @@
         ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
         // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
         // can reset the exception stats.
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeString(entry.getKey());
             e.writeInt(entry.getValue());
             pulledData.add(e);
         }
     }
 
-    private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         LooperStats looperStats = LocalServices.getService(LooperStats.class);
         if (looperStats == null) {
             return;
@@ -977,9 +1029,8 @@
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
         looperStats.reset();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (LooperStats.ExportedEntry entry : entries) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 10 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(1000); // uid collection not implemented yet
             e.writeString(entry.handlerClassName);
             e.writeString(entry.threadName);
@@ -994,7 +1045,8 @@
         }
     }
 
-    private void pullDiskStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         // Run a quick-and-dirty performance test: write 512 bytes
         byte[] junk = new byte[512];
         for (int i = 0; i < junk.length; i++) junk[i] = (byte) i;  // Write nonzero bytes
@@ -1041,41 +1093,40 @@
         }
 
         // Add info pulledData.
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(latency);
         e.writeBoolean(fileBased);
         e.writeInt(writeSpeed);
         pulledData.add(e);
     }
 
-    private void pullDirectoryUsage(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
         StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
         StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
 
-        StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
         e.writeLong(statFsData.getAvailableBytes());
         e.writeLong(statFsData.getTotalBytes());
         pulledData.add(e);
 
-        e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
         e.writeLong(statFsCache.getAvailableBytes());
         e.writeLong(statFsCache.getTotalBytes());
         pulledData.add(e);
 
-        e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
         e.writeLong(statFsSystem.getAvailableBytes());
         e.writeLong(statFsSystem.getTotalBytes());
         pulledData.add(e);
     }
 
-    private void pullAppSize(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         try {
             String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
             JSONObject json = new JSONObject(jsonStr);
@@ -1093,7 +1144,7 @@
             }
             for (int i = 0; i < length; i++) {
                 StatsLogEventWrapper e =
-                        new StatsLogEventWrapper(elapsedNanos, tagId, 5 /* fields */);
+                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
                 e.writeString(pkg_names.getString(i));
                 e.writeLong(app_sizes.optLong(i, -1L));
                 e.writeLong(app_data_sizes.optLong(i, -1L));
@@ -1106,62 +1157,62 @@
         }
     }
 
-    private void pullCategorySize(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         try {
             String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
             JSONObject json = new JSONObject(jsonStr);
             long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
 
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
             e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
             e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
             e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
             e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
             e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
             e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
             e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
             e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
             e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
             e.writeLong(cacheTime);
@@ -1171,110 +1222,139 @@
         }
     }
 
+    private void pullNumFingerprints(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class);
+        if (fingerprintManager == null) {
+            return;
+        }
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        if (userManager == null) {
+            return;
+        }
+        final long token = Binder.clearCallingIdentity();
+        for (UserInfo user : userManager.getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+            final int numFingerprints = fingerprintManager.getEnrolledFingerprints(userId).size();
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(userId);
+            e.writeInt(numFingerprints);
+            pulledData.add(e);
+        }
+        Binder.restoreCallingIdentity(token);
+    }
+
     /**
      * Pulls various data.
      */
     @Override // Binder call
     public StatsLogEventWrapper[] pullData(int tagId) {
         enforceCallingPermission();
-        if (DEBUG)
+        if (DEBUG) {
             Slog.d(TAG, "Pulling " + tagId);
+        }
         List<StatsLogEventWrapper> ret = new ArrayList<>();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
         switch (tagId) {
             case StatsLog.WIFI_BYTES_TRANSFER: {
-                pullWifiBytesTransfer(tagId, ret);
+                pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.MOBILE_BYTES_TRANSFER: {
-                pullMobileBytesTransfer(tagId, ret);
+                pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
-                pullWifiBytesTransferByFgBg(tagId, ret);
+                pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
-                pullMobileBytesTransferByFgBg(tagId, ret);
+                pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
-                pullBluetoothBytesTransfer(tagId, ret);
+                pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.KERNEL_WAKELOCK: {
-                pullKernelWakelock(tagId, ret);
+                pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_TIME_PER_FREQ: {
-                pullCpuTimePerFreq(tagId, ret);
+                pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_TIME_PER_UID: {
-                pullKernelUidCpuTime(tagId, ret);
+                pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_TIME_PER_UID_FREQ: {
-                pullKernelUidCpuFreqTime(tagId, ret);
+                pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_CLUSTER_TIME: {
-                pullKernelUidCpuClusterTime(tagId, ret);
+                pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_ACTIVE_TIME: {
-                pullKernelUidCpuActiveTime(tagId, ret);
+                pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.WIFI_ACTIVITY_INFO: {
-                pullWifiActivityInfo(tagId, ret);
+                pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.MODEM_ACTIVITY_INFO: {
-                pullModemActivityInfo(tagId, ret);
+                pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
-                pullBluetoothActivityInfo(tagId, ret);
+                pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.SYSTEM_UPTIME: {
-                pullSystemUpTime(tagId, ret);
+                pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.SYSTEM_ELAPSED_REALTIME: {
-                pullSystemElapsedRealtime(tagId, ret);
+                pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.PROCESS_MEMORY_STATE: {
-                pullProcessMemoryState(tagId, ret);
+                pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BINDER_CALLS: {
-                pullBinderCallsStats(tagId, ret);
+                pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BINDER_CALLS_EXCEPTIONS: {
-                pullBinderCallsStatsExceptions(tagId, ret);
+                pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.LOOPER_STATS: {
-                pullLooperStats(tagId, ret);
+                pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.DISK_STATS: {
-                pullDiskStats(tagId, ret);
+                pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.DIRECTORY_USAGE: {
-                pullDirectoryUsage(tagId, ret);
+                pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.APP_SIZE: {
-                pullAppSize(tagId, ret);
+                pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CATEGORY_SIZE: {
-                pullCategorySize(tagId, ret);
+                pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.NUM_FINGERPRINTS: {
+                pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             default:
@@ -1486,7 +1566,8 @@
 
     // Thermal event received from vendor thermal management subsystem
     private static final class ThermalEventListener extends IThermalEventListener.Stub {
-        @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
+        @Override
+        public void notifyThrottling(boolean isThrottling, Temperature temp) {
             StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(),
                     isThrottling ? 1 : 0, temp.getValue());
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6dd87f1..984ebe1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -150,7 +150,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
-import com.android.internal.view.IInputMethodClient;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.RotationCache;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -409,6 +408,9 @@
 
     private InputMonitor mInputMonitor;
 
+    /** Caches the value whether told display manager that we have content. */
+    private boolean mLastHasContent;
+
     /**
      * The input method window for this display.
      */
@@ -2946,7 +2948,7 @@
         }
     }
 
-    boolean inputMethodClientHasFocus(IInputMethodClient client) {
+    boolean isInputMethodClientFocus(int uid, int pid) {
         final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
         if (imFocus == null) {
             return false;
@@ -2958,17 +2960,13 @@
             Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
         }
 
-        final IInputMethodClient imeClient = imFocus.mSession.mClient;
-
         if (DEBUG_INPUT_METHOD) {
-            Slog.i(TAG_WM, "IM target client: " + imeClient);
-            if (imeClient != null) {
-                Slog.i(TAG_WM, "IM target client binder: " + imeClient.asBinder());
-                Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
-            }
+            Slog.i(TAG_WM, "IM target uid/pid: " + imFocus.mSession.mUid
+                    + "/" + imFocus.mSession.mPid);
+            Slog.i(TAG_WM, "Requesting client uid/pid: " + uid + "/" + pid);
         }
 
-        return imeClient != null && imeClient.asBinder() == client.asBinder();
+        return imFocus.mSession.mUid == uid && imFocus.mSession.mPid == pid;
     }
 
     boolean hasSecureWindowOnScreen() {
@@ -3092,8 +3090,9 @@
         forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
         prepareSurfaces();
 
+        mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
         mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
-                mTmpApplySurfaceChangesTransactionState.displayHasContent,
+                mLastHasContent,
                 mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
                 mTmpApplySurfaceChangesTransactionState.preferredModeId,
                 true /* inTraversal, must call performTraversalInTrans... below */);
@@ -4267,4 +4266,11 @@
     InputMonitor getInputMonitor() {
         return mInputMonitor;
     }
+
+    /**
+     * @return Cached value whether we told display manager that we have content.
+     */
+    boolean getLastHasContent() {
+        return mLastHasContent;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 685c444..bd82553 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -732,10 +733,13 @@
             boolean shouldUpdateOrientationListener = false;
 
             // Configure rotation suggestions.
-            final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
-                    Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
-                    UserHandle.USER_CURRENT);
+            final int showRotationSuggestions =
+                    ActivityManager.isLowRamDeviceStatic()
+                            ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED
+                            : Settings.Secure.getIntForUser(resolver,
+                            Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+                            Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+                            UserHandle.USER_CURRENT);
             if (mShowRotationSuggestions != showRotationSuggestions) {
                 mShowRotationSuggestions = showRotationSuggestions;
                 shouldUpdateOrientationListener = true;
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index c99329a..0c9a14b 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -21,7 +21,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -35,7 +35,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/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 793ce60..57cae39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,7 +30,6 @@
 import android.view.MagnificationSpec;
 import android.view.WindowInfo;
 
-import com.android.internal.view.IInputMethodClient;
 import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -444,9 +443,14 @@
     public abstract boolean isUidFocused(int uid);
 
     /**
-     * Returns {@code true} if a process that is identified by {@code client} has IME focus.
+     * Checks whether the specified process has IME focus or not.
+     *
+     * @param uid UID of the process to be queried
+     * @param pid PID of the process to be queried
+     * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
+     *         focus
      */
-    public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+    public abstract boolean isInputMethodClientFocus(int uid, int pid);
 
     /**
      * Return the display Id for given window.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2ed09ae..711f66a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7411,32 +7411,32 @@
             }
         }
 
-    public boolean inputMethodClientHasFocus(IInputMethodClient client) {
-        boolean hasFocus;
-        synchronized (mWindowMap) {
-            // Check all displays if any input method window has focus.
-            for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
-                final DisplayContent displayContent = mRoot.mChildren.get(i);
-                if (displayContent.inputMethodClientHasFocus(client)) {
+        @Override
+        public boolean isInputMethodClientFocus(int uid, int pid) {
+            synchronized (mWindowMap) {
+                // Check all displays if any input method window has focus.
+                for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+                    final DisplayContent displayContent = mRoot.mChildren.get(i);
+                    if (displayContent.isInputMethodClientFocus(uid, pid)) {
+                        return true;
+                    }
+                }
+
+                // Okay, how about this...  what is the current focus?
+                // It seems in some cases we may not have moved the IM
+                // target window, such as when it was in a pop-up window,
+                // so let's also look at the current focus.  (An example:
+                // go to Gmail, start searching so the keyboard goes up,
+                // press home.  Sometimes the IME won't go down.)
+                // Would be nice to fix this more correctly, but it's
+                // way at the end of a release, and this should be good enough.
+                if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
+                        && mCurrentFocus.mSession.mPid == pid) {
                     return true;
                 }
             }
-
-            // Okay, how about this...  what is the current focus?
-            // It seems in some cases we may not have moved the IM
-            // target window, such as when it was in a pop-up window,
-            // so let's also look at the current focus.  (An example:
-            // go to Gmail, start searching so the keyboard goes up,
-            // press home.  Sometimes the IME won't go down.)
-            // Would be nice to fix this more correctly, but it's
-            // way at the end of a release, and this should be good enough.
-            if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
-                    && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
-                return true;
-            }
+            return false;
         }
-        return false;
-    }
 
         @Override
         public int getDisplayIdForWindow(IBinder windowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fec8039..979149a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1176,15 +1176,17 @@
                         if (mIsWallpaper) {
                             w.dispatchWallpaperVisibility(true);
                         }
-                        // This draw means the difference between unique content and mirroring.
-                        // Run another pass through performLayout to set mHasContent in the
-                        // LogicalDisplay.
-                        mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                                FINISH_LAYOUT_REDO_ANIM);
-                        if (DEBUG_LAYOUT_REPEATS) {
-                            mService.mWindowPlacerLocked.debugLayoutRepeats(
-                                    "showSurfaceRobustlyLocked " + w,
-                                    mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+                        if (!w.getDisplayContent().getLastHasContent()) {
+                            // This draw means the difference between unique content and mirroring.
+                            // Run another pass through performLayout to set mHasContent in the
+                            // LogicalDisplay.
+                            mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+                                    FINISH_LAYOUT_REDO_ANIM);
+                            if (DEBUG_LAYOUT_REPEATS) {                        
+                                mService.mWindowPlacerLocked.debugLayoutRepeats(
+                                        "showSurfaceRobustlyLocked " + w,
+                                        mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+                            }
                         }
                     } else {
                         w.setOrientationChanging(false);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 2338744..ffc7fa2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -69,7 +69,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityRecordTests extends ActivityTestsBase {
-    private ActivityTaskManagerService mService;
     private TestActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivity;
@@ -79,10 +78,10 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityTaskManagerService();
-        mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        setupActivityTaskManagerService();
+        mStack = mSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
         mActivity = new ActivityBuilder(mService).setTask(mTask).build();
     }
 
@@ -189,17 +188,13 @@
 
     @Test
     public void testCanBeLaunchedOnDisplay() throws Exception {
-        testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
-                true /*activityResizeable*/, true /*expected*/);
+        mService.mSupportsMultiWindow = true;
+        final ActivityRecord activity = new ActivityBuilder(mService).build();
 
-        testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
-                false /*activityResizeable*/, false /*expected*/);
-
-        testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/,
-                true /*activityResizeable*/, false /*expected*/);
-
-        testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/,
-                false /*activityResizeable*/, true /*expected*/);
+        // An activity can be launched on default display.
+        assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
+        // An activity cannot be launched on a non-existent display.
+        assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
     }
 
     @Test
@@ -230,26 +225,4 @@
         assertNull(mActivity.pendingOptions);
         assertNotNull(activity2.pendingOptions);
     }
-
-    private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
-            boolean activityResizeable, boolean expected) {
-        mService.mSupportsMultiWindow = true;
-
-        final TaskRecord task = taskPresent
-                ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
-
-        if (task != null) {
-            task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
-        }
-
-        final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
-        record.info.resizeMode = activityResizeable
-                ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-
-        record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
-
-
-        verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
-                anyInt(), anyInt(), eq(record.info));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1aa80c8..0345a81 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -64,8 +64,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackSupervisorTests extends ActivityTestsBase {
-    private ActivityTaskManagerService mService;
-    private ActivityStackSupervisor mSupervisor;
     private ActivityStack mFullscreenStack;
 
     @Before
@@ -73,8 +71,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityTaskManagerService();
-        mSupervisor = mService.mStackSupervisor;
+        setupActivityTaskManagerService();
         mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 95f8fd1..ab814ee 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -60,8 +60,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackTests extends ActivityTestsBase {
-    private ActivityTaskManagerService mService;
-    private ActivityStackSupervisor mSupervisor;
     private ActivityDisplay mDefaultDisplay;
     private ActivityStack mStack;
     private TaskRecord mTask;
@@ -71,9 +69,8 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityTaskManagerService();
-        mSupervisor = mService.mStackSupervisor;
-        mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
+        setupActivityTaskManagerService();
+        mDefaultDisplay = mSupervisor.getDefaultDisplay();
         mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index d032eb5..749403e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -32,26 +32,32 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 
+import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.WindowLayout;
@@ -65,28 +71,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.view.Gravity;
 
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
-
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -106,7 +90,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStarterTests extends ActivityTestsBase {
-    private ActivityTaskManagerService mService;
     private ActivityStarter mStarter;
     private ActivityStartController mController;
     private ActivityMetricsLogger mActivityMetricsLogger;
@@ -130,7 +113,7 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mService = createActivityTaskManagerService();
+        setupActivityTaskManagerService();
         mController = mock(ActivityStartController.class);
         mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
         clearInvocations(mActivityMetricsLogger);
@@ -323,7 +306,22 @@
         }
     }
 
-    private ActivityStarter prepareStarter(int launchFlags) {
+    private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) {
+        return prepareStarter(launchFlags, true /* mockGetLaunchStack */);
+    }
+
+    /**
+     * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
+     *
+     * @param launchFlags The intent flags to launch activity.
+     * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+     *                           always launching to the testing stack. Set to false when allowing
+     *                           the activity can be launched to any stack that is decided by real
+     *                           implementation.
+     * @return A {@link ActivityStarter} with default setup.
+     */
+    private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
+            boolean mockGetLaunchStack) {
         // always allow test to start activity.
         doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
@@ -343,11 +341,13 @@
         // return task when created.
         doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any());
 
-        // direct starter to use spy stack.
-        doReturn(stack).when(mService.mStackSupervisor)
-                .getLaunchStack(any(), any(), any(), anyBoolean());
-        doReturn(stack).when(mService.mStackSupervisor)
-                .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+        if (mockGetLaunchStack) {
+            // Direct starter to use spy stack.
+            doReturn(stack).when(mService.mStackSupervisor)
+                    .getLaunchStack(any(), any(), any(), anyBoolean());
+            doReturn(stack).when(mService.mStackSupervisor)
+                    .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
+        }
 
         // Set up mock package manager internal and make sure no unmocked methods are called
         PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class,
@@ -546,4 +546,84 @@
                 eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(),
                 any(), eq(false));
     }
+
+    /**
+     * This test ensures that when starting an existing single task activity on secondary display
+     * which is not the top focused display, it should deliver new intent to the activity and not
+     * create a new stack.
+     */
+    @Test
+    public void testDeliverIntentToTopActivityOfNonTopDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetLaunchStack */);
+
+        // Create a secondary display at bottom.
+        final TestActivityDisplay secondaryDisplay = spy(addNewActivityDisplayAt(POSITION_BOTTOM));
+        final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Create an activity record on the top of secondary display.
+        final ComponentName componentName = ComponentName.createRelative(
+                DEFAULT_COMPONENT_PACKAGE_NAME,
+                DEFAULT_COMPONENT_PACKAGE_NAME + ".ReusableActivity");
+        final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
+                .setComponent(componentName)
+                .setStack(stack)
+                .build();
+        final ActivityRecord topActivityOnSecondaryDisplay = new ActivityBuilder(mService)
+                .setComponent(componentName)
+                .setLaunchMode(LAUNCH_SINGLE_TASK)
+                .setTask(taskRecord)
+                .build();
+
+        // Put an activity on default display as the top focused activity.
+        new ActivityBuilder(mService).setCreateTask(true).build();
+
+        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
+        // on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay")
+                .setIntent(topActivityOnSecondaryDisplay.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure result is delivering intent to top.
+        assertEquals(START_DELIVERED_TO_TOP, result);
+
+        // Ensure secondary display only creates one stack.
+        verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean());
+    }
+
+    /**
+     * This test ensures that a reused top activity in the top focused stack is able to be
+     * reparented to another display.
+     */
+    @Test
+    public void testReparentTopFocusedActivityToSecondaryDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetLaunchStack */);
+
+        // Create a secondary display at bottom.
+        final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM);
+        secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+
+        // Put an activity on default display as the top focused activity.
+        final ActivityRecord topActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setLaunchMode(LAUNCH_SINGLE_TASK)
+                .build();
+
+        // Start activity with the same intent as {@code topActivity} on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay")
+                .setIntent(topActivity.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure the activity is moved to secondary display.
+        assertEquals(secondaryDisplay, topActivity.getDisplay());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index bb8e5c5..9d09f5c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -56,11 +57,14 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.testing.DexmakerShareClassLoaderRule;
+import android.view.Display;
+import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -86,6 +90,8 @@
 public class ActivityTestsBase {
     private static boolean sOneTimeSetupDone = false;
 
+    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
+
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
@@ -93,6 +99,9 @@
     private final Context mContext = InstrumentationRegistry.getContext();
     private HandlerThread mHandlerThread;
 
+    ActivityTaskManagerService mService;
+    ActivityStackSupervisor mSupervisor;
+
     // Default package name
     static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
 
@@ -122,6 +131,11 @@
         return atm;
     }
 
+    void setupActivityTaskManagerService() {
+        mService = createActivityTaskManagerService();
+        mSupervisor = mService.mStackSupervisor;
+    }
+
     ActivityManagerService createActivityManagerService() {
         final TestActivityTaskManagerService atm =
                 spy(new TestActivityTaskManagerService(mContext));
@@ -134,6 +148,18 @@
         return am;
     }
 
+    /** Creates a {@link TestActivityDisplay}. */
+    TestActivityDisplay createNewActivityDisplay() {
+        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+    }
+
+    /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+    TestActivityDisplay addNewActivityDisplayAt(int position) {
+        final TestActivityDisplay display = createNewActivityDisplay();
+        mSupervisor.addChild(display, position);
+        return display;
+    }
+
     void setupActivityManagerService(
             TestActivityManagerService am, TestActivityTaskManagerService atm) {
         atm.setActivityManagerService(am);
@@ -173,6 +199,7 @@
         private boolean mCreateTask;
         private ActivityStack mStack;
         private int mActivityFlags;
+        private int mLaunchMode;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -198,6 +225,11 @@
             return this;
         }
 
+        ActivityBuilder setLaunchMode(int launchMode) {
+            mLaunchMode = launchMode;
+            return this;
+        }
+
         ActivityBuilder setStack(ActivityStack stack) {
             mStack = stack;
             return this;
@@ -233,6 +265,7 @@
             aInfo.applicationInfo.packageName = mComponent.getPackageName();
             aInfo.applicationInfo.uid = mUid;
             aInfo.flags |= mActivityFlags;
+            aInfo.launchMode = mLaunchMode;
 
             final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
                     0 /* launchedFromPid */, 0, null, intent, null,
@@ -500,7 +533,7 @@
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY));
+            mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
             addChild(mDisplay, ActivityDisplay.POSITION_TOP);
         }
 
@@ -516,10 +549,20 @@
     }
 
     protected static class TestActivityDisplay extends ActivityDisplay {
-
         private final ActivityStackSupervisor mSupervisor;
-        TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
-            super(supervisor, displayId);
+
+        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+            if (displayId == DEFAULT_DISPLAY) {
+                return new TestActivityDisplay(supervisor,
+                        supervisor.mDisplayManager.getDisplay(displayId));
+            }
+            final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                    new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+            return new TestActivityDisplay(supervisor, display);
+        }
+
+        TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+            super(supervisor, display);
             // Normally this comes from display-properties as exposed by WM. Without that, just
             // hard-code to FULLSCREEN for tests.
             setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 5195214..70cfad1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -870,8 +870,8 @@
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
-            mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+            mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+            mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
             addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
             addChild(mDisplay, ActivityDisplay.POSITION_TOP);
         }
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index d56c6a6..aa3046f 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -51,7 +51,6 @@
 public class RunningTasksTest extends ActivityTestsBase {
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private ActivityTaskManagerService mService;
 
     private RunningTasks mRunningTasks;
 
@@ -60,21 +59,20 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mService = createActivityTaskManagerService();
+        setupActivityTaskManagerService();
         mRunningTasks = new RunningTasks();
     }
 
     @Test
     public void testCollectTasksByLastActiveTime() throws Exception {
         // Create a number of stacks with tasks (of incrementing active time)
-        final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
         final ArrayList<ActivityDisplay> displays = new ArrayList<>();
-        final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
+        final ActivityDisplay display = TestActivityDisplay.create(mSupervisor, DEFAULT_DISPLAY);
         displays.add(display);
 
         final int numStacks = 2;
         for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
-            final ActivityStack stack = new TestActivityStack(display, stackIndex, supervisor,
+            final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
             display.addChild(stack, POSITION_BOTTOM);
         }
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 5039e42..8ac2930 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,18 +21,32 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerSaveState;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+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.BatterySavingStats;
 
 import org.junit.Rule;
 import org.mockito.Mock;
@@ -47,11 +61,19 @@
     private static final boolean BATTERY_SAVER_ENABLED = true;
     private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
 
-    private @Mock BatterySaverPolicy mBatterySaverPolicy;
+    private @Mock BatterySaverPolicy mBatterySaverPolicyMock;
+    private @Mock LightsManager mLightsManagerMock;
+    private @Mock DisplayManagerInternal mDisplayManagerInternalMock;
+    private @Mock BatteryManagerInternal mBatteryManagerInternalMock;
+    private @Mock ActivityManagerInternal mActivityManagerInternalMock;
+    private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock;
+    private @Mock Notifier mNotifierMock;
     private PowerManagerService mService;
     private PowerSaveState mPowerSaveState;
     private DisplayPowerRequest mDisplayPowerRequest;
 
+
+
     @Rule
     public void setUp() throws Exception {
         super.setUp();
@@ -61,11 +83,51 @@
                 .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
                 .setBrightnessFactor(BRIGHTNESS_FACTOR)
                 .build();
-        when(mBatterySaverPolicy.getBatterySaverPolicy(
+        when(mBatterySaverPolicyMock.getBatterySaverPolicy(
                 eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS), anyBoolean()))
                 .thenReturn(mPowerSaveState);
+
         mDisplayPowerRequest = new DisplayPowerRequest();
-        mService = new PowerManagerService(getContext(), mBatterySaverPolicy);
+        addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+        addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+        addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+
+        mService = new PowerManagerService(getContext(), new Injector() {
+            Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                    SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+                return mNotifierMock;
+            }
+
+            SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+                return mock(SuspendBlocker.class);
+            }
+
+            BatterySaverPolicy createBatterySaverPolicy(
+                    Object lock, Context context, BatterySavingStats batterySavingStats) {
+                return mBatterySaverPolicyMock;
+            }
+
+            NativeWrapper createNativeWrapper() {
+                return mNativeWrapperMock;
+            }
+        });
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(LightsManager.class);
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
     }
 
     @SmallTest
@@ -110,6 +172,25 @@
         mService.setVrModeEnabled(false);
         assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
                 DisplayPowerRequest.POLICY_BRIGHT);
+    }
 
+    @SmallTest
+    public void testWakefulnessAwake_InitialValue() throws Exception {
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+    }
+
+    @SmallTest
+    public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+        // Start with AWAKE state
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Take a nap with a flag.
+        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+            PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
 }
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 95d4a15..659c6e7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -20,6 +20,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.any;
@@ -34,6 +35,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -47,6 +49,9 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.SparseArray;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -68,7 +73,9 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 public class ManagedServicesTest extends UiServiceTestCase {
 
@@ -113,7 +120,12 @@
             when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
         }
         when(mUm.getUsers()).thenReturn(users);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+        IntArray profileIds = new IntArray();
+        profileIds.add(0);
+        profileIds.add(11);
+        profileIds.add(10);
+        profileIds.add(12);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
 
         mExpectedPrimaryPackages = new ArrayMap<>();
         mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
@@ -165,12 +177,12 @@
 
     @Test
     public void testBackupAndRestore_migration_preO() throws Exception {
-        ArrayMap backupPrimaryPackages = new ArrayMap<>();
+        ArrayMap<Integer, String> backupPrimaryPackages = new ArrayMap<>();
         backupPrimaryPackages.put(0, "backup.0:backup:0a");
         backupPrimaryPackages.put(10, "10.backup");
         backupPrimaryPackages.put(11, "eleven");
         backupPrimaryPackages.put(12, "");
-        ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+        ArrayMap<Integer, String> backupPrimaryComponentNames = new ArrayMap<>();
         backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
         backupPrimaryComponentNames.put(10, "again/M1");
         backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
@@ -179,11 +191,11 @@
         backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
         backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
 
-        ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+        ArrayMap<Integer, String> backupSecondaryComponentNames = new ArrayMap<>();
         backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
         backupSecondaryComponentNames.put(10,
                 "this.is.another.package.backup/with.Component:component.backup/2");
-        ArrayMap backupSecondaryPackages = new ArrayMap<>();
+        ArrayMap<Integer, String> backupSecondaryPackages = new ArrayMap<>();
         backupSecondaryPackages.put(0, "");
         backupSecondaryPackages.put(10,
                 "this.is.another.package.backup:package.backup");
@@ -368,7 +380,7 @@
             serializer.endDocument();
             serializer.flush();
 
-            for (int userId : mUserProfiles.getCurrentProfileIds()) {
+            for (int userId : mUserProfiles.getCurrentProfileIds().toArray()) {
                 List<String> expected =
                         stringToList(mExpectedPrimary.get(approvalLevel).get(userId));
                 List<String> actual = stringToList(Settings.Secure.getStringForUser(
@@ -633,7 +645,7 @@
     }
 
     @Test
-    public void testGetAllowedComponents() throws Exception {
+    public void testGetAllowedComponentsByUser() throws Exception {
         ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
                 APPROVAL_BY_COMPONENT);
         loadXml(service);
@@ -708,6 +720,145 @@
         assertTrue(services.isSameUser(service, 10));
     }
 
+    @Test
+    public void testGetAllowedComponents() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+        loadXml(service);
+
+        SparseArray<ArraySet<ComponentName>> expected = new SparseArray<>();
+
+        ArraySet<ComponentName> expected10 = new ArraySet<>();
+        expected10.add(ComponentName.unflattenFromString("this.is.another.package/M1"));
+        expected10.add(ComponentName.unflattenFromString("this.is.another.package/with.Component"));
+        expected10.add(ComponentName.unflattenFromString("component/2"));
+        expected10.add(ComponentName.unflattenFromString("package/component2"));
+        expected.put(10, expected10);
+        ArraySet<ComponentName> expected0 = new ArraySet<>();
+        expected0.add(ComponentName.unflattenFromString("secondary/component.Name"));
+        expected0.add(ComponentName.unflattenFromString("this.is.a.package.name/Ba"));
+        expected0.add(ComponentName.unflattenFromString("another.package/B1"));
+        expected.put(0, expected0);
+        ArraySet<ComponentName> expected12 = new ArraySet<>();
+        expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!"));
+        expected.put(12, expected12);
+        expected.put(11, new ArraySet<>());
+
+        SparseArray<ArraySet<ComponentName>> actual =
+                service.getAllowedComponents(mUserProfiles.getCurrentProfileIds());
+
+        assertContentsInAnyOrder(expected, actual);
+    }
+
+    @Test
+    public void testPopulateComponentsToUnbind_forceRebind() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        IInterface iInterface = mock(IInterface.class);
+        when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+        ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+                mock(ServiceConnection.class), 26);
+        ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+                mock(ServiceConnection.class), 26);
+        Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+        removableBoundServices.add(service0);
+        removableBoundServices.add(service10);
+
+        SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+        Set<ComponentName> allowed0 = new ArraySet<>();
+        allowed0.add(ComponentName.unflattenFromString("a/a"));
+        allowedComponentsToBind.put(0, allowed0);
+        Set<ComponentName> allowed10 = new ArraySet<>();
+        allowed10.add(ComponentName.unflattenFromString("b/b"));
+        allowedComponentsToBind.put(10, allowed10);
+
+        SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        service.populateComponentsToUnbind(true, removableBoundServices, allowedComponentsToBind,
+                componentsToUnbind);
+
+        assertEquals(2, componentsToUnbind.size());
+        assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+        assertTrue(componentsToUnbind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+    }
+
+    @Test
+    public void testPopulateComponentsToUnbind() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        IInterface iInterface = mock(IInterface.class);
+        when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+        ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+                mock(ServiceConnection.class), 26);
+        ManagedServices.ManagedServiceInfo service0a = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("c/c"), 0, false,
+                mock(ServiceConnection.class), 26);
+        ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+                mock(ServiceConnection.class), 26);
+        Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+        removableBoundServices.add(service0);
+        removableBoundServices.add(service0a);
+        removableBoundServices.add(service10);
+
+        SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+        Set<ComponentName> allowed0 = new ArraySet<>();
+        allowed0.add(ComponentName.unflattenFromString("a/a"));
+        allowedComponentsToBind.put(0, allowed0);
+        Set<ComponentName> allowed10 = new ArraySet<>();
+        allowed10.add(ComponentName.unflattenFromString("b/b"));
+        allowedComponentsToBind.put(10, allowed10);
+
+        SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        service.populateComponentsToUnbind(false, removableBoundServices, allowedComponentsToBind,
+                componentsToUnbind);
+
+        assertEquals(1, componentsToUnbind.size());
+        assertEquals(1, componentsToUnbind.get(0).size());
+        assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
+    }
+
+    @Test
+    public void populateComponentsToBind() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+        ArraySet<ComponentName> allowed0 = new ArraySet<>();
+        allowed0.add(ComponentName.unflattenFromString("a/a"));
+        approvedComponentsByUser.put(0, allowed0);
+        ArraySet<ComponentName> allowed10 = new ArraySet<>();
+        allowed10.add(ComponentName.unflattenFromString("b/b"));
+        allowed10.add(ComponentName.unflattenFromString("c/c"));
+        approvedComponentsByUser.put(10, allowed10);
+        ArraySet<ComponentName> allowed15 = new ArraySet<>();
+        allowed15.add(ComponentName.unflattenFromString("d/d"));
+        approvedComponentsByUser.put(15, allowed15);
+
+        IntArray users = new IntArray();
+        users.add(10);
+        users.add(0);
+
+        SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+        service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+
+        assertEquals(2, componentsToBind.size());
+        assertEquals(1, componentsToBind.get(0).size());
+        assertTrue(componentsToBind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+        assertEquals(2, componentsToBind.get(10).size());
+        assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+        assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
+    }
+
     private void loadXml(ManagedServices service) throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -775,7 +926,8 @@
                 ManagedServices.ENABLED_SERVICES_SEPARATOR)));
     }
 
-    private void assertContentsInAnyOrder(List<?> expected, List<?> actual) {
+    private void assertContentsInAnyOrder(Collection<?> expected, Collection<?> actual) {
+        assertNotNull(actual);
         assertEquals(expected.size(), actual.size());
 
         for (Object o : expected) {
@@ -787,6 +939,21 @@
         }
     }
 
+    private void assertContentsInAnyOrder(SparseArray<ArraySet<ComponentName>> expected,
+            SparseArray<ArraySet<ComponentName>> actual) throws Exception {
+        assertEquals(expected.size(), actual.size());
+
+        for (int i = 0; i < expected.size(); i++) {
+            int key = expected.keyAt(i);
+            assertTrue(actual.indexOfKey(key) >= 0);
+            try {
+                assertContentsInAnyOrder(expected.valueAt(i), actual.get(key));
+            } catch (Throwable t) {
+                throw new Exception("Error validating " + key, t);
+            }
+        }
+    }
+
     private void verifyExpectedBoundEntries(ManagedServices service, boolean primary)
             throws Exception {
         ArrayMap<Integer, String> verifyMap = primary ? mExpectedPrimary.get(service.mApprovalLevel)
@@ -920,7 +1087,7 @@
 
         @Override
         protected boolean checkType(IInterface service) {
-            return false;
+            return true;
         }
 
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f9a4f78..1de1e4e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -33,6 +33,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
+import android.util.IntArray;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -103,7 +104,12 @@
         }
         when(mUm.getUsers()).thenReturn(users);
         when(mUm.getUsers(anyBoolean())).thenReturn(users);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+        IntArray profileIds = new IntArray();
+        profileIds.add(0);
+        profileIds.add(11);
+        profileIds.add(10);
+        profileIds.add(12);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
     }
 
     @Test
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 88c6fcf..b955e56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -32,6 +32,7 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.IntArray;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -235,18 +236,22 @@
         mSnoozeHelper.snooze(r2, 1000);
         mSnoozeHelper.snooze(r3, 1000);
         mSnoozeHelper.snooze(r4, 1000);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_SYSTEM});
+        IntArray profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_SYSTEM);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         assertEquals(3, mSnoozeHelper.getSnoozed().size());
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_CURRENT});
+        profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_CURRENT);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         assertEquals(1, mSnoozeHelper.getSnoozed().size());
     }
 
     @Test
     public void testGetSnoozedByUser_managedProfiles() throws Exception {
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_SYSTEM, UserHandle.USER_CURRENT});
+        IntArray profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_CURRENT);
+        profileIds.add(UserHandle.USER_SYSTEM);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
         NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
@@ -273,8 +278,9 @@
 
     @Test
     public void repostGroupSummary_repostsSummary() throws Exception {
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_SYSTEM});
+        IntArray profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_SYSTEM);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         NotificationRecord r = getNotificationRecord(
                 "pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
         NotificationRecord r2 = getNotificationRecord(
diff --git a/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
new file mode 100644
index 0000000..03fb123
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.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.server.policy;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.policy.
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyPolicyTests {
+
+    @Presubmit
+    @Test
+    public void preSubmitTest() {}
+
+    @FlakyTest
+    @Presubmit
+    @Test
+    public void flakyPreSubmitTest() {}
+
+    @Test
+    public void postSubmitTest() {}
+
+    @FlakyTest
+    @Test
+    public void flakyPostSubmitTest() {}
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c5d6dc7..99ad1f4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,8 @@
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.AppGlobals;
 import android.content.ComponentName;
@@ -1128,6 +1130,27 @@
             }
         }
 
+        @Override
+        public void getActiveServiceSupportedActions(List<String> voiceActions,
+                IVoiceActionCheckCallback callback) {
+            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+            synchronized (this) {
+                if (mImpl == null) {
+                    try {
+                        callback.onComplete(null);
+                    } catch (RemoteException e) {
+                    }
+                    return;
+                }
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.getActiveServiceSupportedActions(voiceActions, callback);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+        }
+
         public void onSessionShown() {
             synchronized (this) {
                 final int size = mVoiceInteractionSessionListeners.beginBroadcast();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 57e9f66..61d7d6c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -25,6 +25,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
@@ -57,6 +59,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
     final static String TAG = "VoiceInteractionServiceManager";
@@ -177,6 +180,23 @@
                 activityTokens);
     }
 
+    public void getActiveServiceSupportedActions(List<String> commands,
+            IVoiceActionCheckCallback callback) {
+        if (mService == null) {
+            Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
+            try {
+                callback.onComplete(null);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+        try {
+            mService.getActiveServiceSupportedActions(commands, callback);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e);
+        }
+    }
+
     public boolean hideSessionLocked() {
         if (mActiveSession != null) {
             return mActiveSession.hideLocked();
diff --git a/startop/scripts/app_startup/force_compiler_filter b/startop/scripts/app_startup/force_compiler_filter
index 78e915b..08f983d 100755
--- a/startop/scripts/app_startup/force_compiler_filter
+++ b/startop/scripts/app_startup/force_compiler_filter
@@ -100,36 +100,6 @@
   fi
 }
 
-get_activity_name() {
-  local package="$1"
-  local action_key="android.intent.action.MAIN:"
-
-  local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")"
-  verbose_print $activity_line
-  IFS="/" read -a array <<< "$activity_line"
-  local activity_name="${array[1]}"
-  echo "$activity_name"
-  #adb shell am start "$package/$activity_name"
-}
-
-remote_pidof() {
-  local procname="$1"
-  adb shell ps | grep "$procname" | awk '{print $2;}'
-}
-
-remote_pkill() {
-  local procname="$1"
-  shift
-
-  local the_pids=$(remote_pidof "$procname")
-  local pid
-
-  for pid in $the_pids; do
-    verbose_print adb shell kill "$@" "$pid"
-    adb shell kill "$@" "$pid"
-  done
-}
-
 force_package_compilation() {
   local arg_compiler_filter="$1"
   local arg_package="$2"
@@ -150,13 +120,13 @@
     # screen needs to be unlocked in order to run an app
     "$DIR"/unlock_screen
 
-    am_output="$(adb shell am start -S -W "$package"/"$activity")"
+    local output=$("$DIR"/launch_application "$package" "$activity")
     if [[ $? -ne 0 ]]; then
-      echo "am start failed" >&2
+      echo "launching application failed" >&2
       exit 1
     fi
 
-    verbose_print "$am_output"
+    verbose_print "$output"
     # give some time for app startup to complete.
     # this is supposed to be an upper bound for measuring startup time.
     sleep "$wait_time"
diff --git a/startop/scripts/app_startup/launch_application b/startop/scripts/app_startup/launch_application
index bc4ec51..8a68e50 100755
--- a/startop/scripts/app_startup/launch_application
+++ b/startop/scripts/app_startup/launch_application
@@ -20,6 +20,12 @@
 launch_application() {
   local package="$1"
   local activity="$2"
+
+  # if there's any $s inside of the activity name, it needs to be escaped to \$.
+  # example '.app.honeycomb.Shell$HomeActivity'
+  # if the $ is not escaped, adb shell will try to evaluate $HomeActivity to a variable.
+  activity=${activity//\$/\\$}
+
   local am_output="$(adb shell am start -S -W "$package"/"$activity")"
   verbose_print adb shell am start -S -W "$package"/"$activity"
   if [[ $? -ne 0 ]]; then
diff --git a/startop/scripts/app_startup/lib/common b/startop/scripts/app_startup/lib/common
index 4d5a53e..043d855 100755
--- a/startop/scripts/app_startup/lib/common
+++ b/startop/scripts/app_startup/lib/common
@@ -12,3 +12,42 @@
     echo "$@" >&2
   fi
 }
+
+remote_pidof() {
+  local procname="$1"
+  adb shell ps | grep "$procname" | awk '{print $2;}'
+}
+
+remote_pkill() {
+  local procname="$1"
+  shift
+
+  local the_pids=$(remote_pidof "$procname")
+  local pid
+
+  for pid in $the_pids; do
+    verbose_print adb shell kill "$@" "$pid"
+    adb shell kill "$@" "$pid"
+  done
+}
+
+get_activity_name() {
+  local package="$1"
+  local action_key="android.intent.action.MAIN:"
+
+  # Example query-activities output being parsed:
+  #
+  #  Activity #14:
+  #    priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+  #    com.google.android.videos/com.google.android.youtube.videos.EntryPoint
+  #  Activity #15:
+  #    priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
+  #    com.google.android.youtube/.app.honeycomb.Shell$HomeActivity
+
+  # Given package 'com.google.android.youtube' return '.app.honeycomb.Shell$HomeActivity'
+
+  local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package/")"
+  IFS="/" read -a array <<< "$activity_line"
+  local activity_name="${array[1]}"
+  echo "$activity_name"
+}
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index ce63ff9..56bffa8 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -111,18 +111,6 @@
   echo "$@"
 }
 
-get_activity_name() {
-  local package="$1"
-  local action_key="android.intent.action.MAIN:"
-
-  local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")"
-  #echo $activity_line
-  IFS="/" read -a array <<< "$activity_line"
-  local activity_name="${array[1]}"
-  echo "$activity_name"
-  #adb shell am start "$package/$activity_name"
-}
-
 find_package_path() {
   local pkg="$1"
 
@@ -133,11 +121,6 @@
   echo "$res"
 }
 
-remote_pkill() {
-  local what="$1"
-  adb shell "for i in $(pid $what); do kill \$i; done"
-}
-
 # Main entry point
 if [[ $# -eq 0 ]]; then
   usage
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ffbe7d3..7506d00 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1070,6 +1070,16 @@
 
     /**
      * Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats.
+     *
+     * <p>Available options are:
+     * <ul>
+     * <li> 0: %s</li>
+     * <li> 1: %s Wi-Fi Calling</li>
+     * <li> 2: WLAN Call</li>
+     * <li> 3: %s WLAN Call</li>
+     * <li> 4: %s Wi-Fi</li>
+     * <li> 5: WiFi Calling | %s</li>
+     * <li> 6: %s VoWifi</li>
      * @hide
      */
     public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
@@ -1077,6 +1087,15 @@
     public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
 
     /**
+     * Use root locale when reading wfcSpnFormats.
+     *
+     * If true, then the root locale will always be used when reading wfcSpnFormats. This means the
+     * non localized version of wfcSpnFormats will be used.
+     * @hide
+     */
+    public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale";
+
+    /**
      * The Component Name of the activity that can setup the emergency addrees for WiFi Calling
      * as per carrier requirement.
      * @hide
@@ -2263,6 +2282,7 @@
         sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
         sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
         sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putBoolean(KEY_WFC_SPN_USE_ROOT_LOCALE, false);
         sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
         sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
@@ -2272,7 +2292,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
-        sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b01d419..e9423f7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -60,6 +60,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
@@ -1811,6 +1812,19 @@
      */
     @UnsupportedAppUsage
     public static Resources getResourcesForSubId(Context context, int subId) {
+        return getResourcesForSubId(context, subId, false);
+    }
+
+    /**
+     * Returns the resources associated with Subscription.
+     * @param context Context object
+     * @param subId Subscription Id of Subscription who's resources are required
+     * @param useRootLocale if root locale should be used. Localized locale is used if false.
+     * @return Resources associated with Subscription.
+     * @hide
+     */
+    public static Resources getResourcesForSubId(Context context, int subId,
+            boolean useRootLocale) {
         final SubscriptionInfo subInfo =
                 SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
 
@@ -1822,6 +1836,11 @@
             newConfig.mnc = subInfo.getMnc();
             if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
         }
+
+        if (useRootLocale) {
+            newConfig.setLocale(Locale.ROOT);
+        }
+
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         DisplayMetrics newMetrics = new DisplayMetrics();
         newMetrics.setTo(metrics);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fa24796..b5d1f06 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6217,36 +6217,25 @@
     /**
      * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     public boolean endCall() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.endCall();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#endCall", e);
-        }
         return false;
     }
 
     /**
      * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void answerRingingCall() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.answerRingingCall();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#answerRingingCall", e);
-        }
+
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 6bf22a0..8015b07 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -33,6 +33,7 @@
 import com.android.internal.telephony.SmsConstants;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Locale;
 
 /**
  * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
@@ -44,16 +45,34 @@
      * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
      */
     private static final String[] LANGUAGE_CODES_GROUP_0 = {
-            "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
-            "pl", null
+            Locale.GERMAN.getLanguage(),        // German
+            Locale.ENGLISH.getLanguage(),       // English
+            Locale.ITALIAN.getLanguage(),       // Italian
+            Locale.FRENCH.getLanguage(),        // French
+            new Locale("es").getLanguage(),     // Spanish
+            new Locale("nl").getLanguage(),     // Dutch
+            new Locale("sv").getLanguage(),     // Swedish
+            new Locale("da").getLanguage(),     // Danish
+            new Locale("pt").getLanguage(),     // Portuguese
+            new Locale("fi").getLanguage(),     // Finnish
+            new Locale("nb").getLanguage(),     // Norwegian
+            new Locale("el").getLanguage(),     // Greek
+            new Locale("tr").getLanguage(),     // Turkish
+            new Locale("hu").getLanguage(),     // Hungarian
+            new Locale("pl").getLanguage(),     // Polish
+            null
     };
 
     /**
      * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
      */
     private static final String[] LANGUAGE_CODES_GROUP_2 = {
-            "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
-            null, null
+            new Locale("cs").getLanguage(),     // Czech
+            new Locale("he").getLanguage(),     // Hebrew
+            new Locale("ar").getLanguage(),     // Arabic
+            new Locale("ru").getLanguage(),     // Russian
+            new Locale("is").getLanguage(),     // Icelandic
+            null, null, null, null, null, null, null, null, null, null, null
     };
 
     private static final char CARRIAGE_RETURN = 0x0d;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 4790b75..6b3df94 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -93,11 +93,23 @@
      *     converted. If the array size is more than needed, the rest of array remains unchanged.
      */
     public static void bcdToBytes(String bcd, byte[] bytes) {
+        bcdToBytes(bcd, bytes, 0);
+    }
+
+    /**
+     * Converts BCD string to bytes and put it into the given byte array.
+     *
+     * @param bcd This should have an even length. If not, an "0" will be appended to the string.
+     * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
+     *     converted. If the array size is more than needed, the rest of array remains unchanged.
+     * @param offset the offset into the bytes[] to fill the data
+     */
+    public static void bcdToBytes(String bcd, byte[] bytes, int offset) {
         if (bcd.length() % 2 != 0) {
             bcd += "0";
         }
-        int size = Math.min(bytes.length * 2, bcd.length());
-        for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
+        int size = Math.min((bytes.length - offset) * 2, bcd.length());
+        for (int i = 0, j = offset; i + 1 < size; i += 2, j++) {
             bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
         }
     }
@@ -125,6 +137,20 @@
     }
 
     /**
+     * Convert a 5 or 6 - digit PLMN string to a nibble-swizzled encoding as per 24.008 10.5.1.3
+     *
+     * @param plmn the PLMN to convert
+     * @param data a byte array for the output
+     * @param offset the offset into data to start writing
+     */
+    public static void stringToBcdPlmn(final String plmn, byte[] data, int offset) {
+        char digit6 = (plmn.length() > 5) ? plmn.charAt(5) : 'F';
+        data[offset] = (byte) (charToByte(plmn.charAt(1)) << 4 | charToByte(plmn.charAt(0)));
+        data[offset + 1] = (byte) (charToByte(digit6) << 4 | charToByte(plmn.charAt(2)));
+        data[offset + 2] = (byte) (charToByte(plmn.charAt(4)) << 4 | charToByte(plmn.charAt(3)));
+    }
+
+    /**
      * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
      */
     public static String
@@ -844,7 +870,7 @@
     }
 
     /**
-     * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
+     * Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
      * hex number, 0 will be returned.
      */
     private static byte charToByte(char c) {
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
index 1b63874..6500428 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -16,24 +16,27 @@
 
 package com.android.tests.sysmem.host;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
 /**
  * Critical user journeys with which to exercise the system, driven from the
  * host.
  */
 public class Cujs {
-    private ITestDevice mDevice;
+    private Device mDevice;
 
-    public Cujs(ITestDevice device) {
+    public Cujs(Device device) {
         this.mDevice = device;
     }
 
     /**
      * Runs the critical user journeys.
      */
-    public void run() throws DeviceNotAvailableException {
+    public void run() throws TestException {
+        // Do an explicit GC in the system server process as part of the test
+        // case to reduce GC-related sources of noise.
+        // SIGUSR1 = 10 is the magic signal to trigger the GC.
+        int pid = mDevice.getPidForProcess("system_server");
+        mDevice.executeShellCommand("kill -10 " + pid);
+
         // Invoke the Device Cujs instrumentation to run the cujs.
         // TODO: Consider exercising the system in other interesting ways as
         // well.
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
new file mode 100644
index 0000000..03503ce
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Wrapper around ITestDevice exposing useful device functions.
+ */
+class Device {
+
+    private ITestDevice mDevice;
+
+    Device(ITestDevice device) {
+        mDevice = device;
+    }
+
+    /**
+     * Execute a shell command and return the output as a string.
+     */
+    public String executeShellCommand(String command) throws TestException {
+        try {
+            return mDevice.executeShellCommand(command);
+        } catch (DeviceNotAvailableException e) {
+            throw new TestException(e);
+        }
+    }
+
+    /**
+     * Enable adb root
+     */
+    public void enableAdbRoot() throws TestException {
+        try {
+            mDevice.enableAdbRoot();
+        } catch (DeviceNotAvailableException e) {
+            throw new TestException(e);
+        }
+    }
+
+    /**
+     * Returns the pid for the process with the given name.
+     */
+    public int getPidForProcess(String name) throws TestException {
+        String psout = executeShellCommand("ps -A -o PID,CMD");
+        Scanner sc = new Scanner(psout);
+        try {
+            // ps output is of the form:
+            //  PID CMD
+            //    1 init
+            //    2 kthreadd
+            //    ...
+            // 9693 ps
+            sc.nextLine();
+            while (sc.hasNextLine()) {
+                int pid = sc.nextInt();
+                String cmd = sc.next();
+
+                if (name.equals(cmd)) {
+                    return pid;
+                }
+            }
+        } catch (InputMismatchException e) {
+            throw new TestException("unexpected ps output format: " + psout, e);
+        }
+
+        throw new TestException("failed to get pid for process " + name);
+    }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
index cfd598d..48b7f39 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
@@ -16,7 +16,6 @@
 
 package com.android.tests.sysmem.host;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
@@ -27,8 +26,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.IOException;
-
 /**
  * Runs a system memory test.
  */
@@ -44,8 +41,9 @@
     private Cujs mCujs;
 
     @Override
-    public void setDevice(ITestDevice device) {
-        mTestDevice = device;
+    public void setDevice(ITestDevice testDevice) {
+        mTestDevice = testDevice;
+        Device device = new Device(testDevice);
         mMetrics = new Metrics(device, testMetrics, testLogs);
         mCujs = new Cujs(device);
     }
@@ -56,14 +54,13 @@
     }
 
     // Invoke a single iteration of running the cujs.
-    private void runCujs() throws DeviceNotAvailableException {
+    private void runCujs() throws TestException {
         mCujs.run();
         mIterations++;
     }
 
     // Sample desired memory.
-    private void sample()
-            throws DeviceNotAvailableException, IOException, Metrics.MetricsException {
+    private void sample() throws TestException {
         mMetrics.sample(String.format("%03d", mIterations));
     }
 
@@ -71,7 +68,7 @@
      * Runs the memory tests.
      */
     @Test
-    public void run() throws Exception {
+    public void run() throws TestException {
         sample();   // Sample before running cujs
         runCujs();
         sample();   // Sample after first iteration of cujs
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
index 70bc22c..b408a81 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -16,8 +16,6 @@
 
 package com.android.tests.sysmem.host;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.FileInputStreamSource;
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
@@ -34,24 +32,11 @@
  */
 class Metrics {
 
-    private ITestDevice mDevice;
+    private Device mDevice;
     private TestMetrics mMetrics;
     private TestLogData mLogs;
 
     /**
-     * Exception thrown in case of error sampling metrics.
-     */
-    public static class MetricsException extends Exception {
-        MetricsException(String msg) {
-            super(msg);
-        }
-
-        MetricsException(String msg, Exception cause) {
-            super(msg, cause);
-        }
-    }
-
-    /**
      * Constructs a metrics instance that will output high level metrics and
      * more detailed breakdowns using the given <code>metrics</code> and
      * <code>logs</code> objects.
@@ -60,7 +45,7 @@
      * @param metrics where to log the high level metrics when taking a sample
      * @param logs where to log detailed breakdowns when taking a sample
      */
-    Metrics(ITestDevice device, TestMetrics metrics, TestLogData logs) {
+    Metrics(Device device, TestMetrics metrics, TestLogData logs) {
         this.mDevice = device;
         this.mMetrics = metrics;
         this.mLogs = logs;
@@ -69,43 +54,17 @@
     /**
      * Writes the given <code>text</code> to a log with the given label.
      */
-    private void logText(String label, String text) throws IOException {
-        File file = File.createTempFile(label, "txt");
-        PrintStream ps = new PrintStream(file);
-        ps.print(text);
-        try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
-            mLogs.addTestLog(label, LogDataType.TEXT, dataStream);
-        }
-    }
-
-    /**
-     * Returns the pid for the process with the given name.
-     */
-    private int getPidForProcess(String name)
-            throws DeviceNotAvailableException, IOException, MetricsException {
-        String psout = mDevice.executeShellCommand("ps -A -o PID,CMD");
-        Scanner sc = new Scanner(psout);
+    private void logText(String label, String text) throws TestException {
         try {
-            // ps output is of the form:
-            //  PID CMD
-            //    1 init
-            //    2 kthreadd
-            //    ...
-            // 9693 ps
-            sc.nextLine();
-            while (sc.hasNextLine()) {
-                int pid = sc.nextInt();
-                String cmd = sc.next();
-
-                if (name.equals(cmd)) {
-                    return pid;
-                }
+            File file = File.createTempFile(label, "txt");
+            PrintStream ps = new PrintStream(file);
+            ps.print(text);
+            try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+                mLogs.addTestLog(label, LogDataType.TEXT, dataStream);
             }
-        } catch (InputMismatchException e) {
-            throw new MetricsException("unexpected ps output format: " + psout, e);
+        } catch (IOException e) {
+            throw new TestException(e);
         }
-
-        throw new MetricsException("failed to get pid for process " + name);
     }
 
     /**
@@ -116,11 +75,11 @@
      *
      * @param label prefix to use for metrics and logs output for this sample.
      */
-    void sample(String label) throws DeviceNotAvailableException, IOException, MetricsException {
+    void sample(String label) throws TestException {
         // adb root access is required to get showmap
         mDevice.enableAdbRoot();
 
-        int pid = getPidForProcess("system_server");
+        int pid = mDevice.getPidForProcess("system_server");
 
         // Read showmap for system server and add it as a test log
         String showmap = mDevice.executeShellCommand("showmap " + pid);
@@ -146,7 +105,7 @@
             mMetrics.addTestMetric(label + ".system_server.rss", Long.toString(rss));
             mMetrics.addTestMetric(label + ".system_server.pss", Long.toString(pss));
         } catch (InputMismatchException e) {
-            throw new MetricsException("unexpected showmap format", e);
+            throw new TestException("unexpected showmap format", e);
         }
 
         // Run debuggerd -j to get GC stats for system server and add it as a
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java
new file mode 100644
index 0000000..dc3b68f
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/TestException.java
@@ -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.
+ */
+
+package com.android.tests.sysmem.host;
+
+/**
+ * Exception thrown in case of unexpected error encountered when executing the
+ * test.
+ */
+class TestException extends Exception {
+    TestException(Exception cause) {
+        super(cause);
+    }
+
+    TestException(String msg) {
+        super(msg);
+    }
+
+    TestException(String msg, Exception cause) {
+        super(msg, cause);
+    }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 2ecf25b..c02ca21 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -83,6 +83,7 @@
         "compile/Pseudolocalizer.cpp",
         "compile/XmlIdCollector.cpp",
         "configuration/ConfigurationParser.cpp",
+        "dump/DumpManifest.cpp",
         "filter/AbiFilter.cpp",
         "filter/ConfigFilter.cpp",
         "format/Archive.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index a73d56c..a20b9b7 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -69,8 +69,8 @@
       return {};
     }
 
-    io::ZeroCopyInputAdaptor adaptor(in.get());
-    if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+    io::ProtoInputStreamReader proto_reader(in.get());
+    if (!proto_reader.ReadMessage(&pb_table)) {
       diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
       return {};
     }
@@ -97,8 +97,8 @@
   }
 
   pb::XmlNode pb_node;
-  io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
-  if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+  io::ProtoInputStreamReader proto_reader(manifest_in.get());
+  if (!proto_reader.ReadMessage(&pb_node)) {
     diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
     return {};
   }
@@ -255,7 +255,7 @@
 }
 
 std::unique_ptr<xml::XmlResource> LoadedApk::LoadXml(const std::string& file_path,
-                                                     IDiagnostics* diag) {
+                                                     IDiagnostics* diag) const {
   io::IFile* file = apk_->FindFile(file_path);
   if (file == nullptr) {
     diag->Error(DiagMessage() << "failed to find file");
@@ -270,9 +270,9 @@
       return nullptr;
     }
 
-    io::ZeroCopyInputAdaptor adaptor(in.get());
     pb::XmlNode pb_node;
-    if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+    io::ProtoInputStreamReader proto_reader(in.get());
+    if (!proto_reader.ReadMessage(&pb_node)) {
       diag->Error(DiagMessage() << "failed to parse file as proto XML");
       return nullptr;
     }
@@ -317,8 +317,8 @@
     std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
     if (manifest_in != nullptr) {
       pb::XmlNode pb_node;
-      io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
-      if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+      io::ProtoInputStreamReader proto_reader(manifest_in.get());
+      if (!proto_reader.ReadMessage(&pb_node)) {
         return ApkFormat::kProto;
       }
     }
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index dcb085a..84c57c1 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -111,7 +111,7 @@
                               IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
 
   /** Loads the file as an xml document. */
-  std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, IDiagnostics* diag);
+  std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, IDiagnostics* diag) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedApk);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 86b1f4c..954f1ed 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -170,8 +170,8 @@
       }
 
       pb::XmlNode pb_node;
-      io::ZeroCopyInputAdaptor adaptor(in.get());
-      if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+      io::ProtoInputStreamReader proto_reader(in.get());
+      if (!proto_reader.ReadMessage(&pb_node)) {
         context_->GetDiagnostics()->Error(DiagMessage(source_)
                                           << "failed to parse proto XML " << *file->path);
         return false;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 5cb30b6..91e3977 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -223,6 +223,12 @@
     return 1;
   }
 
+  ResourceTable* table = loaded_apk->GetResourceTable();
+  if (!table) {
+    diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+    return 1;
+  }
+
   io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
   Printer printer(&fout);
 
@@ -233,7 +239,7 @@
 
   // Insert the configurations into a set in order to keep every configuarion seen
   std::set<ConfigDescription, decltype(compare)> configs(compare);
-  for (auto& package : loaded_apk->GetResourceTable()->packages) {
+  for (auto& package : table->packages) {
     for (auto& type : package->types) {
       for (auto& entry : type->entries) {
         for (auto& value : entry->values) {
@@ -267,10 +273,15 @@
       return 1;
     }
 
+    ResourceTable* table = loaded_apk->GetResourceTable();
+    if (!table) {
+      diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+      return 1;
+    }
+
     // Load the run-time xml string pool using the flattened data
     BigBuffer buffer(4096);
-    StringPool::FlattenUtf8(&buffer, loaded_apk->GetResourceTable()->string_pool,
-                            context.GetDiagnostics());
+    StringPool::FlattenUtf8(&buffer, table->string_pool, context.GetDiagnostics());
     auto data = buffer.to_string();
     android::ResStringPool pool(data.data(), data.size(), false);
     Debug::DumpResStringPool(&pool, &printer);
@@ -304,7 +315,13 @@
       printer.Println("Binary APK");
     }
 
-    Debug::PrintTable(*loaded_apk->GetResourceTable(), print_options, &printer);
+    ResourceTable* table = loaded_apk->GetResourceTable();
+    if (!table) {
+      diag_->Error(DiagMessage() << "Failed to retrieve resource table.");
+      return 1;
+    }
+
+    Debug::PrintTable(*table, print_options, &printer);
   }
 
   return 0;
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 0724d62..9ec820d 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -19,6 +19,7 @@
 
 #include "Command.h"
 #include "Debug.h"
+#include "dump/DumpManifest.h"
 
 namespace aapt {
 
@@ -133,8 +134,10 @@
  public:
   explicit DumpCommand(IDiagnostics* diag) : Command("dump", "d"), diag_(diag) {
     AddOptionalSubcommand(util::make_unique<DumpAPCCommand>(diag_));
+    AddOptionalSubcommand(util::make_unique<DumpBadgingCommand>(diag_));
     AddOptionalSubcommand(util::make_unique<DumpConfigsCommand>(diag_));
     AddOptionalSubcommand(util::make_unique<DumpPackageNameCommand>(diag_));
+    AddOptionalSubcommand(util::make_unique<DumpPermissionsCommand>(diag_));
     AddOptionalSubcommand(util::make_unique<DumpStringsCommand>(diag_));
     AddOptionalSubcommand(util::make_unique<DumpTableCommand>(diag_));
     AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(diag_));
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 119f56a..13c1047 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1842,9 +1842,15 @@
     } else {
       // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
       // equal to the minSdk.
+      const size_t origConstraintSize = options_.split_constraints.size();
       options_.split_constraints =
           AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
 
+      if (origConstraintSize != options_.split_constraints.size()) {
+        context_->GetDiagnostics()->Warn(DiagMessage()
+                                         << "requested to split resources prior to min sdk of "
+                                         << context_->GetMinSdkVersion());
+      }
       TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options);
       if (!table_splitter.VerifySplitConstraints(context_)) {
         return 1;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index c6c82b0..5862d31 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -73,6 +73,7 @@
   }
 
   *out_path = parts[0];
+  out_split->name = parts[1];
   for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) {
     ConfigDescription config;
     if (!ConfigDescription::Parse(config_str, &config)) {
@@ -119,12 +120,15 @@
   for (const SplitConstraints& constraints : split_constraints) {
     SplitConstraints constraint;
     for (const ConfigDescription& config : constraints.configs) {
-      if (config.sdkVersion <= min_sdk) {
-        constraint.configs.insert(config.CopyWithoutSdkVersion());
-      } else {
-        constraint.configs.insert(config);
+      const ConfigDescription &configToInsert = (config.sdkVersion <= min_sdk)
+          ? config.CopyWithoutSdkVersion()
+          : config;
+      // only add the config if it actually selects something
+      if (configToInsert != ConfigDescription::DefaultConfig()) {
+        constraint.configs.insert(configToInsert);
       }
     }
+    constraint.name = constraints.name;
     adjusted_constraints.push_back(std::move(constraint));
   }
   return adjusted_constraints;
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index b9fb5b2..158ef29 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -22,6 +22,10 @@
 #include "test/Test.h"
 
 namespace aapt {
+#define EXPECT_CONFIG_EQ(constraints, config) \
+    EXPECT_EQ(constraints.configs.size(), 1); \
+    EXPECT_EQ(*constraints.configs.begin(), config); \
+    constraints.configs.clear();
 
 TEST(UtilTest, SplitNamesAreSanitized) {
     AppInfo app_info{"com.pkg"};
@@ -84,4 +88,287 @@
   EXPECT_EQ(compiled_version_code_major->value.data, 0x61);
 }
 
+
+TEST (UtilTest, ParseSplitParameter) {
+  IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics();
+  std::string path;
+  SplitConstraints constraints;
+  ConfigDescription expected_configuration;
+
+  // ========== Test IMSI ==========
+  // mcc: 'mcc[0-9]{3}'
+  // mnc: 'mnc[0-9]{1,3}'
+  ASSERT_TRUE(ParseSplitParameter(":mcc310",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setMcc(0x0136)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setMcc(0x0136)
+      .setMnc(0x0004)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setMcc(0x0136)
+      .setMnc(0xFFFF)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test LOCALE ==========
+  // locale: '[a-z]{2,3}(-r[a-z]{2})?'
+  // locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?'
+  ASSERT_TRUE(ParseSplitParameter(":es",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setLanguage(0x6573)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":fr-rCA",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setLanguage(0x6672)
+      .setCountry(0x4341)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":b+es+419",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setLanguage(0x6573)
+      .setCountry(0xA424)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test SCREEN_TYPE ==========
+  // orientation: '(port|land|square)'
+  // touchscreen: '(notouch|stylus|finger)'
+  // density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)'
+  ASSERT_TRUE(ParseSplitParameter(":square",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setOrientation(0x03)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":stylus",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setTouchscreen(0x02)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":xxxhdpi",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setDensity(0x0280)
+      .setSdkVersion(0x0004) // version [any density requires donut]
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setOrientation(0x02)
+      .setTouchscreen(0x03)
+      .setDensity(0x0140)
+      .setSdkVersion(0x0004) // version [any density requires donut]
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test INPUT ==========
+  // keyboard: '(nokeys|qwerty|12key)'
+  // navigation: '(nonav|dpad|trackball|wheel)'
+  // inputFlags: '(keysexposed|keyshidden|keyssoft)'
+  // inputFlags: '(navexposed|navhidden)'
+  ASSERT_TRUE(ParseSplitParameter(":qwerty",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setKeyboard(0x02)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":dpad",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setNavigation(0x02)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setInputFlags(0x0B)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setKeyboard(0x01)
+      .setNavigation(0x03)
+      .setInputFlags(0x06)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test SCREEN_SIZE ==========
+  // screenWidth/screenHeight: '[0-9]+x[0-9]+'
+  ASSERT_TRUE(ParseSplitParameter(":1920x1080",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenWidth(0x0780)
+      .setScreenHeight(0x0438)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test VERSION ==========
+  // version 'v[0-9]+'
+
+  // ========== Test SCREEN_CONFIG ==========
+  // screenLayout [direction]: '(ldltr|ldrtl)'
+  // screenLayout [size]: '(small|normal|large|xlarge)'
+  // screenLayout [long]: '(long|notlong)'
+  // uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)'
+  // uiMode [night]: '(night|notnight)'
+  // smallestScreenWidthDp: 'sw[0-9]dp'
+  ASSERT_TRUE(ParseSplitParameter(":ldrtl",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenLayout(0x80)
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":small",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenLayout(0x01)
+      .setSdkVersion(0x0004) // screenLayout (size) requires donut
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":notlong",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenLayout(0x10)
+      .setSdkVersion(0x0004) // screenLayout (long) requires donut
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenLayout(0x62)
+      .setSdkVersion(0x0004) // screenLayout (size|long) requires donut
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":car",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setUiMode(0x03)
+      .setSdkVersion(0x0008) // uiMode requires froyo
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":vrheadset",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setUiMode(0x07)
+      .setSdkVersion(0x001A) // uiMode 'vrheadset' requires oreo
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":television-night",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setUiMode(0x24)
+      .setSdkVersion(0x0008) // uiMode requires froyo
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":sw1920dp",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setSmallestScreenWidthDp(0x0780)
+      .setSdkVersion(0x000D) // smallestScreenWidthDp requires honeycomb mr2
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test SCREEN_SIZE_DP ==========
+  // screenWidthDp: 'w[0-9]dp'
+  // screenHeightDp: 'h[0-9]dp'
+  ASSERT_TRUE(ParseSplitParameter(":w1920dp",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenWidthDp(0x0780)
+      .setSdkVersion(0x000D) // screenWidthDp requires honeycomb mr2
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":h1080dp",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenHeightDp(0x0438)
+      .setSdkVersion(0x000D) // screenHeightDp requires honeycomb mr2
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  // ========== Test SCREEN_CONFIG_2 ==========
+  // screenLayout2: '(round|notround)'
+  // colorMode: '(widecg|nowidecg)'
+  // colorMode: '(highhdr|lowdr)'
+  ASSERT_TRUE(ParseSplitParameter(":round",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setScreenLayout2(0x02)
+      .setSdkVersion(0x0017) // screenLayout2 (round) requires marshmallow
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+
+  ASSERT_TRUE(ParseSplitParameter(":widecg-highdr",
+                                  diagnostics, &path, &constraints));
+  expected_configuration = test::ConfigDescriptionBuilder()
+      .setColorMode(0x0A)
+      .setSdkVersion(0x001A) // colorMode (hdr|colour gamut) requires oreo
+      .Build();
+  EXPECT_CONFIG_EQ(constraints, expected_configuration);
+}
+
+TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+  IDiagnostics* diagnostics = context.get()->GetDiagnostics();
+  std::vector<SplitConstraints> test_constraints;
+  std::string path;
+
+  test_constraints.push_back({});
+  ASSERT_TRUE(ParseSplitParameter(":v7",
+                                  diagnostics, &path, &test_constraints.back()));
+  test_constraints.push_back({});
+  ASSERT_TRUE(ParseSplitParameter(":xhdpi",
+                                  diagnostics, &path, &test_constraints.back()));
+  EXPECT_EQ(test_constraints.size(), 2);
+  EXPECT_EQ(test_constraints[0].name, "v7");
+  EXPECT_EQ(test_constraints[0].configs.size(), 1);
+  EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+  EXPECT_EQ(test_constraints[1].name, "xhdpi");
+  EXPECT_EQ(test_constraints[1].configs.size(), 1);
+  EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig());
+
+  auto adjusted_contraints = AdjustSplitConstraintsForMinSdk(26, test_constraints);
+  EXPECT_EQ(adjusted_contraints.size(), 2);
+  EXPECT_EQ(adjusted_contraints[0].name, "v7");
+  EXPECT_EQ(adjusted_contraints[0].configs.size(), 0);
+  EXPECT_EQ(adjusted_contraints[1].name, "xhdpi");
+  EXPECT_EQ(adjusted_contraints[1].configs.size(), 1);
+  EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
new file mode 100644
index 0000000..2c356d1
--- /dev/null
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -0,0 +1,2197 @@
+/*
+ * 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 "DumpManifest.h"
+
+#include "LoadedApk.h"
+#include "SdkConstants.h"
+#include "ValueVisitor.h"
+#include "io/File.h"
+#include "io/FileStream.h"
+#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
+
+using ::android::base::StringPrintf;
+
+namespace aapt {
+
+/**
+ * These are attribute resource constants for the platform, as found in android.R.attr.
+ */
+enum {
+  LABEL_ATTR = 0x01010001,
+  ICON_ATTR = 0x01010002,
+  NAME_ATTR = 0x01010003,
+  PERMISSION_ATTR = 0x01010006,
+  EXPORTED_ATTR = 0x01010010,
+  GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
+  RESOURCE_ATTR = 0x01010025,
+  DEBUGGABLE_ATTR = 0x0101000f,
+  VALUE_ATTR = 0x01010024,
+  VERSION_CODE_ATTR = 0x0101021b,
+  VERSION_NAME_ATTR = 0x0101021c,
+  SCREEN_ORIENTATION_ATTR = 0x0101001e,
+  MIN_SDK_VERSION_ATTR = 0x0101020c,
+  MAX_SDK_VERSION_ATTR = 0x01010271,
+  REQ_TOUCH_SCREEN_ATTR = 0x01010227,
+  REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
+  REQ_HARD_KEYBOARD_ATTR = 0x01010229,
+  REQ_NAVIGATION_ATTR = 0x0101022a,
+  REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
+  TARGET_SDK_VERSION_ATTR = 0x01010270,
+  TEST_ONLY_ATTR = 0x01010272,
+  ANY_DENSITY_ATTR = 0x0101026c,
+  GL_ES_VERSION_ATTR = 0x01010281,
+  SMALL_SCREEN_ATTR = 0x01010284,
+  NORMAL_SCREEN_ATTR = 0x01010285,
+  LARGE_SCREEN_ATTR = 0x01010286,
+  XLARGE_SCREEN_ATTR = 0x010102bf,
+  REQUIRED_ATTR = 0x0101028e,
+  INSTALL_LOCATION_ATTR = 0x010102b7,
+  SCREEN_SIZE_ATTR = 0x010102ca,
+  SCREEN_DENSITY_ATTR = 0x010102cb,
+  REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
+  COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
+  LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
+  PUBLIC_KEY_ATTR = 0x010103a6,
+  CATEGORY_ATTR = 0x010103e8,
+  BANNER_ATTR = 0x10103f2,
+  ISGAME_ATTR = 0x10103f4,
+  REQUIRED_FEATURE_ATTR = 0x1010557,
+  REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+  COMPILE_SDK_VERSION_ATTR = 0x01010572,
+  COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
+};
+
+const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
+
+/** Retrieves the attribute of the element with the specified attribute resource id. */
+static xml::Attribute* FindAttribute(xml::Element *el, uint32_t resd_id) {
+  for (auto& a : el->attributes) {
+    if (a.compiled_attribute && a.compiled_attribute.value().id) {
+      if (a.compiled_attribute.value().id.value() == resd_id) {
+        return std::move(&a);
+      }
+    }
+  }
+  return nullptr;
+}
+
+/** Retrieves the attribute of the element that has the specified namespace and attribute name. */
+static xml::Attribute* FindAttribute(xml::Element *el, const std::string &package,
+                                     const std::string &name) {
+  return el->FindAttribute(package, name);
+}
+
+class CommonFeatureGroup;
+
+class ManifestExtractor {
+ public:
+  struct Options {
+    /** Include meta information from <meta-data> elements in the output. */
+    bool include_meta_data = false;
+
+    /** Only output permission information. */
+    bool only_permissions = false;
+  };
+
+  explicit ManifestExtractor(LoadedApk* apk, ManifestExtractor::Options options)
+      : apk_(apk), options_(options) { }
+
+  class Element {
+   public:
+    Element() = default;
+    virtual ~Element() = default;
+
+    static std::unique_ptr<Element> Inflate(ManifestExtractor* extractor, xml::Element* el);
+
+    /** Writes out the extracted contents of the element. */
+    virtual void Print(text::Printer& printer) { }
+
+    /** Adds an element to the list of children of the element. */
+    void AddChild(std::unique_ptr<Element>& child) { children_.push_back(std::move(child)); }
+
+    /** Retrieves the list of children of the element. */
+    const std::vector<std::unique_ptr<Element>>& children() const {
+      return children_;
+    }
+
+    /** Retrieves the extracted xml element tag. */
+    const std::string tag() const {
+      return tag_;
+    }
+
+   protected:
+    ManifestExtractor* extractor() const {
+      return extractor_;
+    }
+
+    /** Retrieves and stores the information extracted from the xml element. */
+    virtual void Extract(xml::Element* el) { }
+
+    /*
+     * Retrieves a configuration value of the resource entry that best matches the specified
+     * configuration.
+     */
+    static Value* BestConfigValue(ResourceEntry* entry,
+                                  const ConfigDescription& match) {
+      if (!entry) {
+        return nullptr;
+      }
+
+      // Determine the config that best matches the desired config
+      ResourceConfigValue* best_value = nullptr;
+      for (auto& value : entry->values) {
+        if (!value->config.match(match)) {
+          continue;
+        }
+
+        if (best_value != nullptr) {
+          if (!value->config.isBetterThan(best_value->config, &match)) {
+            if (value->config.compare(best_value->config) != 0) {
+              continue;
+            }
+          }
+        }
+
+        best_value = value.get();
+      }
+
+      // The entry has no values
+      if (!best_value) {
+        return nullptr;
+      }
+
+      return best_value->value.get();
+    }
+
+    /** Retrieves the resource assigned to the specified resource id if one exists. */
+    Value* FindValueById(const ResourceTable* table, const ResourceId& res_id,
+                         const ConfigDescription& config = DummyConfig()) {
+      if (table) {
+        for (auto& package : table->packages) {
+          if (package->id && package->id.value() == res_id.package_id()) {
+            for (auto& type : package->types) {
+              if (type->id && type->id.value() == res_id.type_id()) {
+                for (auto& entry : type->entries) {
+                  if (entry->id && entry->id.value() == res_id.entry_id()) {
+                    if (auto value = BestConfigValue(entry.get(), config)) {
+                      return value;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      return nullptr;
+    }
+
+    /** Attempts to resolve the reference to a non-reference value. */
+    Value* ResolveReference(Reference* ref, const ConfigDescription& config = DummyConfig()) {
+      const int kMaxIterations = 40;
+      int i = 0;
+      while (ref && ref->id && i++ < kMaxIterations) {
+        auto table = extractor_->apk_->GetResourceTable();
+        if (auto value = FindValueById(table, ref->id.value(), config)) {
+          if (ValueCast<Reference>(value)) {
+            ref = ValueCast<Reference>(value);
+          } else {
+            return value;
+          }
+        }
+      }
+      return nullptr;
+    }
+
+    /**
+     * Retrieves the integer value of the attribute . If the value of the attribute is a reference,
+     * this will attempt to resolve the reference to an integer value.
+     **/
+    int32_t* GetAttributeInteger(xml::Attribute* attr,
+                                 const ConfigDescription& config = DummyConfig()) {
+      if (attr != nullptr) {
+        if (attr->compiled_value) {
+          // Resolve references using the dummy configuration
+          Value* value = attr->compiled_value.get();
+          if (ValueCast<Reference>(value)) {
+            value = ResolveReference(ValueCast<Reference>(value), config);
+          } else {
+            value = attr->compiled_value.get();
+          }
+          // Retrieve the integer data if possible
+          if (value != nullptr) {
+            if (BinaryPrimitive* intValue = ValueCast<BinaryPrimitive>(value)) {
+              return (int32_t*) &intValue->value.data;
+            }
+          }
+        }
+      }
+      return nullptr;
+    }
+
+    /**
+     * A version of GetAttributeInteger that returns a default integer if the attribute does not
+     * exist or cannot be resolved to an integer value.
+     **/
+    int32_t GetAttributeIntegerDefault(xml::Attribute* attr, int32_t def,
+                                       const ConfigDescription& config = DummyConfig()) {
+      auto value = GetAttributeInteger(attr, config);
+      if (value) {
+        return *value;
+      }
+      return def;
+    }
+
+    /**
+     * Retrieves the string value of the attribute. If the value of the attribute is a reference,
+     * this will attempt to resolve the reference to a string value.
+     **/
+    const std::string* GetAttributeString(xml::Attribute* attr,
+                                          const ConfigDescription& config = DummyConfig()) {
+      if (attr != nullptr) {
+        if (attr->compiled_value) {
+          // Resolve references using the dummy configuration
+          Value* value = attr->compiled_value.get();
+          if (ValueCast<Reference>(value)) {
+            value = ResolveReference(ValueCast<Reference>(value), config);
+          } else {
+            value = attr->compiled_value.get();
+          }
+
+          // Retrieve the string data of the value if possible
+          if (value != nullptr) {
+            if (String* intValue = ValueCast<String>(value)) {
+              return &(*intValue->value);
+            } else if (RawString* rawValue = ValueCast<RawString>(value)) {
+              return &(*rawValue->value);
+            } else if (FileReference* strValue = ValueCast<FileReference>(value)) {
+              return &(*strValue->path);
+            }
+          }
+        }
+        return &attr->value;
+      }
+      return nullptr;
+    }
+
+    /**
+     * A version of GetAttributeString that returns a default string if the attribute does not
+     * exist or cannot be resolved to an string value.
+     **/
+    std::string GetAttributeStringDefault(xml::Attribute* attr, std::string def,
+                                          const ConfigDescription& config = DummyConfig()) {
+      auto value = GetAttributeString(attr, config);
+      if (value) {
+        return *value;
+      }
+      return def;
+    }
+
+   private:
+      ManifestExtractor* extractor_;
+      std::vector<std::unique_ptr<Element>> children_;
+      std::string tag_;
+  };
+
+  friend Element;
+
+  /** Creates a default configuration used to retrieve resources. */
+  static ConfigDescription DummyConfig() {
+    ConfigDescription config;
+    config.orientation = android::ResTable_config::ORIENTATION_PORT;
+    config.density = android::ResTable_config::DENSITY_MEDIUM;
+    config.sdkVersion = 10000; // Very high.
+    config.screenWidthDp = 320;
+    config.screenHeightDp = 480;
+    config.smallestScreenWidthDp = 320;
+    config.screenLayout |= android::ResTable_config::SCREENSIZE_NORMAL;
+    return config;
+  }
+
+  bool Dump(text::Printer& printer, IDiagnostics* diag);
+
+  /** Recursively visit the xml element tree and return a processed badging element tree. */
+  std::unique_ptr<Element> Visit(xml::Element* element);
+
+    /** Raises the target sdk value if the min target is greater than the current target. */
+  void RaiseTargetSdk(int32_t min_target) {
+    if (min_target > target_sdk_) {
+      target_sdk_ = min_target;
+    }
+  }
+
+  /**
+   * Retrieves the default feature group that features are added into when <uses-feature>
+   * are not in a <feature-group> element.
+   **/
+  CommonFeatureGroup* GetCommonFeatureGroup() {
+    return commonFeatureGroup_.get();
+  }
+
+  /**
+   * Retrieves a mapping of density values to Configurations for retrieving resources that would be
+   * used for that density setting.
+   **/
+  const std::map<uint16_t, ConfigDescription> densities() const {
+    return densities_;
+  }
+
+  /**
+   * Retrieves a mapping of locale BCP 47 strings to Configurations for retrieving resources that
+   * would be used for that locale setting.
+   **/
+  const std::map<std::string, ConfigDescription> locales() const {
+    return locales_;
+  }
+
+  /** Retrieves the current stack of parent during data extraction. */
+  const std::vector<Element*> parent_stack() const {
+    return parent_stack_;
+  }
+
+  int32_t target_sdk() const {
+    return target_sdk_;
+  }
+
+  LoadedApk* const apk_;
+  const Options options_;
+
+ private:
+  std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>();
+  std::map<std::string, ConfigDescription> locales_;
+  std::map<uint16_t, ConfigDescription> densities_;
+  std::vector<Element*> parent_stack_;
+  int32_t target_sdk_ = 0;
+};
+
+template<typename T> T* ElementCast(ManifestExtractor::Element* element);
+
+/** Recurs through the children of the specified root in depth-first order. */
+static void ForEachChild(ManifestExtractor::Element* root,
+                         std::function<void(ManifestExtractor::Element*)> f) {
+  for (auto& child : root->children()) {
+    f(child.get());
+    ForEachChild(child.get(), f);
+  }
+}
+
+/**
+ * Checks the element and its recursive children for an element that makes the specified
+ * conditional function return true. Returns the first element that makes the conditional function
+ * return true.
+ **/
+static ManifestExtractor::Element* FindElement(ManifestExtractor::Element* root,
+                                              std::function<bool(ManifestExtractor::Element*)> f) {
+  if (f(root)) {
+    return root;
+  }
+  for (auto& child : root->children()) {
+    if (auto b2 = FindElement(child.get(), f)) {
+      return b2;
+    }
+  }
+  return nullptr;
+}
+
+/** Represents the <manifest> elements **/
+class Manifest : public ManifestExtractor::Element {
+ public:
+  Manifest() = default;
+  std::string package;
+  int32_t versionCode;
+  std::string versionName;
+  const std::string* split = nullptr;
+  const std::string* platformVersionName = nullptr;
+  const std::string* platformVersionCode = nullptr;
+  const int32_t* compilesdkVersion = nullptr;
+  const std::string* compilesdkVersionCodename = nullptr;
+  const int32_t* installLocation = nullptr;
+
+  void Extract(xml::Element* manifest) override {
+    package = GetAttributeStringDefault(FindAttribute(manifest, {}, "package"), "");
+    versionCode = GetAttributeIntegerDefault(FindAttribute(manifest, VERSION_CODE_ATTR), 0);
+    versionName = GetAttributeStringDefault(FindAttribute(manifest, VERSION_NAME_ATTR), "");
+    split = GetAttributeString(FindAttribute(manifest, {}, "split"));
+
+    // Extract the platform build info
+    platformVersionName = GetAttributeString(FindAttribute(manifest, {},
+                                                           "platformBuildVersionName"));
+    platformVersionCode = GetAttributeString(FindAttribute(manifest, {},
+                                                           "platformBuildVersionCode"));
+
+    // Extract the compile sdk info
+    compilesdkVersion = GetAttributeInteger(FindAttribute(manifest, COMPILE_SDK_VERSION_ATTR));
+    compilesdkVersionCodename = GetAttributeString(
+        FindAttribute(manifest, COMPILE_SDK_VERSION_CODENAME_ATTR));
+    installLocation = GetAttributeInteger(FindAttribute(manifest, INSTALL_LOCATION_ATTR));
+  }
+
+  void Print(text::Printer& printer) override {
+    printer.Print(StringPrintf("package: name='%s' ", package.data()));
+    printer.Print(StringPrintf("versionCode='%s' ",
+                               (versionCode > 0) ? std::to_string(versionCode).data() : ""));
+    printer.Print(StringPrintf("versionName='%s'", versionName.data()));
+
+    if (split) {
+      printer.Print(StringPrintf(" split='%s'", split->data()));
+    }
+    if (platformVersionName) {
+      printer.Print(StringPrintf(" platformBuildVersionName='%s'", platformVersionName->data()));
+    }
+    if (platformVersionCode) {
+      printer.Print(StringPrintf(" platformBuildVersionCode='%s'", platformVersionCode->data()));
+    }
+    if (compilesdkVersion) {
+      printer.Print(StringPrintf(" compileSdkVersion='%d'", *compilesdkVersion));
+    }
+    if (compilesdkVersionCodename) {
+      printer.Print(StringPrintf(" compileSdkVersionCodename='%s'",
+                                 compilesdkVersionCodename->data()));
+    }
+    printer.Print("\n");
+
+    if (installLocation) {
+      switch (*installLocation) {
+        case 0:
+          printer.Print("install-location:'auto'\n");
+          break;
+        case 1:
+          printer.Print("install-location:'internalOnly'\n");
+          break;
+        case 2:
+          printer.Print("install-location:'preferExternal'\n");
+          break;
+        default:
+          break;
+      }
+    }
+  }
+};
+
+/** Represents <application> elements. **/
+class Application : public ManifestExtractor::Element {
+ public:
+  Application() = default;
+  std::string label;
+  std::string icon;
+  std::string banner;
+  int32_t is_game;
+  int32_t debuggable;
+  int32_t test_only;
+  bool has_multi_arch;
+
+  /** Mapping from locales to app names. */
+  std::map<std::string, std::string> locale_labels;
+
+  /** Mapping from densities to app icons. */
+  std::map<uint16_t, std::string> density_icons;
+
+  void Extract(xml::Element* element) override {
+    label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+    icon = GetAttributeStringDefault(FindAttribute(element, ICON_ATTR), "");
+    test_only = GetAttributeIntegerDefault(FindAttribute(element, TEST_ONLY_ATTR), 0);
+    banner = GetAttributeStringDefault(FindAttribute(element, BANNER_ATTR), "");
+    is_game = GetAttributeIntegerDefault(FindAttribute(element, ISGAME_ATTR), 0);
+    debuggable = GetAttributeIntegerDefault(FindAttribute(element, DEBUGGABLE_ATTR), 0);
+
+    // We must search by name because the multiArch flag hasn't been API
+    // frozen yet.
+    has_multi_arch = (GetAttributeIntegerDefault(
+        FindAttribute(element, kAndroidNamespace, "multiArch"), 0) != 0);
+
+    // Retrieve the app names for every locale the app supports
+    auto attr = FindAttribute(element, LABEL_ATTR);
+    for (auto& config : extractor()->locales()) {
+      if (auto label = GetAttributeString(attr, config.second)) {
+        if (label) {
+          locale_labels.insert(std::make_pair(config.first, *label));
+        }
+      }
+    }
+
+    // Retrieve the icons for the densities the app supports
+    attr = FindAttribute(element, ICON_ATTR);
+    for (auto& config : extractor()->densities()) {
+      if (auto resource = GetAttributeString(attr, config.second)) {
+        if (resource) {
+          density_icons.insert(std::make_pair(config.first, *resource));
+        }
+      }
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    // Print the labels for every locale
+    for (auto p : locale_labels) {
+      if (p.first.empty()) {
+        printer.Print(StringPrintf("application-label:'%s'\n",
+                                    android::ResTable::normalizeForOutput(p.second.data())
+                                        .c_str()));
+      } else {
+        printer.Print(StringPrintf("application-label-%s:'%s'\n", p.first.data(),
+                                    android::ResTable::normalizeForOutput(p.second.data())
+                                        .c_str()));
+      }
+    }
+
+    // Print the icon paths for every density
+    for (auto p : density_icons) {
+      printer.Print(StringPrintf("application-icon-%d:'%s'\n", p.first, p.second.data()));
+    }
+
+    // Print the application info
+    printer.Print(StringPrintf("application: label='%s' ",
+                                android::ResTable::normalizeForOutput(label.data()).c_str()));
+    printer.Print(StringPrintf("icon='%s'", icon.data()));
+    if (!banner.empty()) {
+      printer.Print(StringPrintf(" banner='%s'", banner.data()));
+    }
+    printer.Print("\n");
+
+    if (test_only != 0) {
+      printer.Print(StringPrintf("testOnly='%d'\n", test_only));
+    }
+    if (is_game != 0) {
+      printer.Print("application-isGame\n");
+    }
+    if (debuggable != 0) {
+      printer.Print("application-debuggable\n");
+    }
+  }
+};
+
+/** Represents <uses-sdk> elements. **/
+class UsesSdkBadging : public ManifestExtractor::Element {
+ public:
+  UsesSdkBadging() = default;
+  const int32_t* min_sdk = nullptr;
+  const std::string* min_sdk_name = nullptr;
+  const int32_t* max_sdk = nullptr;
+  const int32_t* target_sdk = nullptr;
+  const std::string* target_sdk_name = nullptr;
+
+  void Extract(xml::Element* element) override {
+    min_sdk = GetAttributeInteger(FindAttribute(element, MIN_SDK_VERSION_ATTR));
+    min_sdk_name = GetAttributeString(FindAttribute(element, MIN_SDK_VERSION_ATTR));
+    max_sdk = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
+    target_sdk = GetAttributeInteger(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+    target_sdk_name = GetAttributeString(FindAttribute(element, TARGET_SDK_VERSION_ATTR));
+
+    // Detect the target sdk of the element
+    if  ((min_sdk_name && *min_sdk_name == "Donut")
+        || (target_sdk_name && *target_sdk_name == "Donut")) {
+      extractor()->RaiseTargetSdk(4);
+    }
+    if (min_sdk) {
+      extractor()->RaiseTargetSdk(*min_sdk);
+    }
+    if (target_sdk) {
+      extractor()->RaiseTargetSdk(*target_sdk);
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    if (min_sdk) {
+      printer.Print(StringPrintf("sdkVersion:'%d'\n", *min_sdk));
+    } else if (min_sdk_name) {
+      printer.Print(StringPrintf("sdkVersion:'%s'\n", min_sdk_name->data()));
+    }
+    if (max_sdk) {
+      printer.Print(StringPrintf("maxSdkVersion:'%d'\n", *max_sdk));
+    }
+    if (target_sdk) {
+      printer.Print(StringPrintf("targetSdkVersion:'%d'\n", *target_sdk));
+    } else if (target_sdk_name) {
+      printer.Print(StringPrintf("targetSdkVersion:'%s'\n", target_sdk_name->data()));
+    }
+  }
+};
+
+/** Represents <uses-configuration> elements. **/
+class UsesConfiguarion : public ManifestExtractor::Element {
+ public:
+  UsesConfiguarion() = default;
+  int32_t req_touch_screen = 0;
+  int32_t req_keyboard_type = 0;
+  int32_t req_hard_keyboard = 0;
+  int32_t req_navigation = 0;
+  int32_t req_five_way_nav = 0;
+
+  void Extract(xml::Element* element) override {
+    req_touch_screen = GetAttributeIntegerDefault(
+        FindAttribute(element, REQ_TOUCH_SCREEN_ATTR), 0);
+    req_keyboard_type = GetAttributeIntegerDefault(
+        FindAttribute(element, REQ_KEYBOARD_TYPE_ATTR), 0);
+    req_hard_keyboard = GetAttributeIntegerDefault(
+        FindAttribute(element, REQ_HARD_KEYBOARD_ATTR), 0);
+    req_navigation = GetAttributeIntegerDefault(
+        FindAttribute(element, REQ_NAVIGATION_ATTR), 0);
+    req_five_way_nav = GetAttributeIntegerDefault(
+        FindAttribute(element, REQ_FIVE_WAY_NAV_ATTR), 0);
+  }
+
+  void Print(text::Printer& printer) override {
+    printer.Print("uses-configuration:");
+    if (req_touch_screen != 0) {
+      printer.Print(StringPrintf(" reqTouchScreen='%d'", req_touch_screen));
+    }
+    if (req_keyboard_type != 0) {
+      printer.Print(StringPrintf(" reqKeyboardType='%d'", req_keyboard_type));
+    }
+    if (req_hard_keyboard != 0) {
+      printer.Print(StringPrintf(" reqHardKeyboard='%d'", req_hard_keyboard));
+    }
+    if (req_navigation != 0) {
+      printer.Print(StringPrintf(" reqNavigation='%d'", req_navigation));
+    }
+    if (req_five_way_nav != 0) {
+      printer.Print(StringPrintf(" reqFiveWayNav='%d'", req_five_way_nav));
+    }
+    printer.Print("\n");
+  }
+};
+
+/** Represents <supports-screen> elements. **/
+class SupportsScreen : public ManifestExtractor::Element {
+ public:
+  SupportsScreen() = default;
+  int32_t small_screen = 1;
+  int32_t normal_screen = 1;
+  int32_t large_screen  = 1;
+  int32_t xlarge_screen = 1;
+  int32_t any_density = 1;
+  int32_t requires_smallest_width_dp = 0;
+  int32_t compatible_width_limit_dp = 0;
+  int32_t largest_width_limit_dp = 0;
+
+  void Extract(xml::Element* element) override {
+    small_screen = GetAttributeIntegerDefault(FindAttribute(element, SMALL_SCREEN_ATTR), 1);
+    normal_screen = GetAttributeIntegerDefault(FindAttribute(element, NORMAL_SCREEN_ATTR), 1);
+    large_screen = GetAttributeIntegerDefault(FindAttribute(element, LARGE_SCREEN_ATTR), 1);
+    xlarge_screen = GetAttributeIntegerDefault(FindAttribute(element, XLARGE_SCREEN_ATTR), 1);
+    any_density = GetAttributeIntegerDefault(FindAttribute(element, ANY_DENSITY_ATTR), 1);
+
+    requires_smallest_width_dp = GetAttributeIntegerDefault(
+        FindAttribute(element, REQUIRES_SMALLEST_WIDTH_DP_ATTR), 0);
+    compatible_width_limit_dp = GetAttributeIntegerDefault(
+        FindAttribute(element, COMPATIBLE_WIDTH_LIMIT_DP_ATTR), 0);
+    largest_width_limit_dp = GetAttributeIntegerDefault(
+        FindAttribute(element, LARGEST_WIDTH_LIMIT_DP_ATTR), 0);
+
+    // For modern apps, if screen size buckets haven't been specified
+    // but the new width ranges have, then infer the buckets from them.
+    if (small_screen > 0 && normal_screen > 0 && large_screen > 0 && xlarge_screen > 0
+        && requires_smallest_width_dp > 0) {
+      int32_t compat_width = (compatible_width_limit_dp > 0) ? compatible_width_limit_dp
+                                                             : requires_smallest_width_dp;
+      small_screen = (requires_smallest_width_dp <= 240 && compat_width >= 240) ? -1 : 0;
+      normal_screen = (requires_smallest_width_dp <= 320 && compat_width >= 320) ? -1 : 0;
+      large_screen = (requires_smallest_width_dp <= 480 && compat_width >= 480) ? -1 : 0;
+      xlarge_screen = (requires_smallest_width_dp <= 720 && compat_width >= 720) ? -1 : 0;
+    }
+  }
+
+  void PrintScreens(text::Printer& printer, int32_t target_sdk) {
+    int32_t small_screen_temp = small_screen;
+    int32_t normal_screen_temp  = normal_screen;
+    int32_t large_screen_temp  = large_screen;
+    int32_t xlarge_screen_temp  = xlarge_screen;
+    int32_t any_density_temp  = any_density;
+
+    // Determine default values for any unspecified screen sizes,
+    // based on the target SDK of the package.  As of 4 (donut)
+    // the screen size support was introduced, so all default to
+    // enabled.
+    if (small_screen_temp  > 0) {
+      small_screen_temp  = target_sdk >= 4 ? -1 : 0;
+    }
+    if (normal_screen_temp  > 0) {
+      normal_screen_temp  = -1;
+    }
+    if (large_screen_temp  > 0) {
+      large_screen_temp  = target_sdk >= 4 ? -1 : 0;
+    }
+    if (xlarge_screen_temp  > 0) {
+      // Introduced in Gingerbread.
+      xlarge_screen_temp  = target_sdk >= 9 ? -1 : 0;
+    }
+    if (any_density_temp  > 0) {
+      any_density_temp  = (target_sdk >= 4 || requires_smallest_width_dp > 0
+          || compatible_width_limit_dp > 0) ? -1 : 0;
+    }
+
+    // Print the formatted screen info
+    printer.Print("supports-screens:");
+    if (small_screen_temp  != 0) {
+      printer.Print(" 'small'");
+    }
+    if (normal_screen_temp  != 0) {
+      printer.Print(" 'normal'");
+    }
+    if (large_screen_temp   != 0) {
+      printer.Print(" 'large'");
+    }
+    if (xlarge_screen_temp  != 0) {
+      printer.Print(" 'xlarge'");
+    }
+    printer.Print("\n");
+    printer.Print(StringPrintf("supports-any-density: '%s'\n",
+                                (any_density_temp ) ? "true" : "false"));
+    if (requires_smallest_width_dp > 0) {
+      printer.Print(StringPrintf("requires-smallest-width:'%d'\n", requires_smallest_width_dp));
+    }
+    if (compatible_width_limit_dp > 0) {
+      printer.Print(StringPrintf("compatible-width-limit:'%d'\n", compatible_width_limit_dp));
+    }
+    if (largest_width_limit_dp > 0) {
+      printer.Print(StringPrintf("largest-width-limit:'%d'\n", largest_width_limit_dp));
+    }
+  }
+};
+
+/** Represents <feature-group> elements. **/
+class FeatureGroup : public ManifestExtractor::Element {
+ public:
+  FeatureGroup() = default;
+  std::string label;
+  int32_t open_gles_version = 0;
+
+  void Extract(xml::Element* element) override {
+    label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+  }
+
+  virtual void PrintGroup(text::Printer& printer) {
+    printer.Print(StringPrintf("feature-group: label='%s'\n", label.data()));
+    if (open_gles_version > 0) {
+      printer.Print(StringPrintf("  uses-gl-es: '0x%x'\n", open_gles_version));
+    }
+
+    for (auto feature : features_) {
+      printer.Print(StringPrintf("  uses-feature%s: name='%s'",
+                                 (feature.second.required ? "" : "-not-required"),
+                                 feature.first.data()));
+      if (feature.second.version > 0) {
+        printer.Print(StringPrintf(" version='%d'", feature.second.version));
+      }
+      printer.Print("\n");
+    }
+  }
+
+  /** Adds a feature to the feature group. */
+  void AddFeature(const std::string& name, bool required = true, int32_t version = -1) {
+    features_.insert(std::make_pair(name, Feature{ required, version }));
+    if (required) {
+      if (name == "android.hardware.camera.autofocus" ||
+          name == "android.hardware.camera.flash") {
+        AddFeature("android.hardware.camera", true);
+      } else if (name == "android.hardware.location.gps" ||
+                 name == "android.hardware.location.network") {
+        AddFeature("android.hardware.location", true);
+      } else if (name == "android.hardware.faketouch.multitouch") {
+        AddFeature("android.hardware.faketouch", true);
+      } else if (name == "android.hardware.faketouch.multitouch.distinct" ||
+                 name == "android.hardware.faketouch.multitouch.jazzhands") {
+        AddFeature("android.hardware.faketouch.multitouch", true);
+        AddFeature("android.hardware.faketouch", true);
+      } else if (name == "android.hardware.touchscreen.multitouch") {
+        AddFeature("android.hardware.touchscreen", true);
+      } else if (name == "android.hardware.touchscreen.multitouch.distinct" ||
+                 name == "android.hardware.touchscreen.multitouch.jazzhands") {
+        AddFeature("android.hardware.touchscreen.multitouch", true);
+        AddFeature("android.hardware.touchscreen", true);
+      } else if (name == "android.hardware.opengles.aep") {
+        const int kOpenGLESVersion31 = 0x00030001;
+        if (kOpenGLESVersion31 > open_gles_version) {
+          open_gles_version = kOpenGLESVersion31;
+        }
+      }
+    }
+  }
+
+  /** Returns true if the feature group has the given feature. */
+  virtual bool HasFeature(const std::string& name) {
+    return features_.find(name) != features_.end();
+  }
+
+  /** Merges the features of another feature group into this group. */
+  void Merge(FeatureGroup* group) {
+    open_gles_version = std::max(open_gles_version, group->open_gles_version);
+    for (auto& feature : group->features_) {
+      features_.insert(feature);
+    }
+  }
+
+ protected:
+  struct Feature {
+   public:
+    bool required = false;
+    int32_t version = -1;
+  };
+
+  /* Mapping of feature names to their properties. */
+  std::map<std::string, Feature> features_;
+};
+
+/**
+ * Represents the default feature group for the application if no <feature-group> elements are
+ * present in the manifest.
+ **/
+class CommonFeatureGroup : public FeatureGroup {
+ public:
+  CommonFeatureGroup() = default;
+  void PrintGroup(text::Printer& printer) override {
+    FeatureGroup::PrintGroup(printer);
+
+    // Also print the implied features
+    for (auto feature : implied_features_) {
+      if (features_.find(feature.first) == features_.end()) {
+        const char* sdk23 = feature.second.implied_from_sdk_k23 ? "-sdk-23" : "";
+        printer.Print(StringPrintf("  uses-feature%s: name='%s'\n", sdk23, feature.first.data()));
+        printer.Print(StringPrintf("  uses-implied-feature%s: name='%s' reason='", sdk23,
+                                    feature.first.data()));
+
+        // Print the reasons as a sentence
+        size_t count = 0;
+        for (auto reason : feature.second.reasons) {
+          printer.Print(reason);
+          if (count + 2 < feature.second.reasons.size()) {
+            printer.Print(", ");
+          } else if (count + 1 < feature.second.reasons.size()) {
+            printer.Print(", and ");
+          }
+          count++;
+        }
+        printer.Print("'\n");
+      }
+    }
+  }
+
+  /** Returns true if the feature group has the given feature. */
+  bool HasFeature(const std::string& name) override {
+    return FeatureGroup::HasFeature(name)
+        || implied_features_.find(name) != implied_features_.end();
+  }
+
+  /** Adds a feature to a set of implied features not explicitly requested in the manifest. */
+  void addImpliedFeature(const std::string& name, const std::string& reason, bool sdk23 = false) {
+    auto entry = implied_features_.find(name);
+    if (entry == implied_features_.end()) {
+      implied_features_.insert(std::make_pair(name, ImpliedFeature(sdk23)));
+      entry = implied_features_.find(name);
+    }
+
+    // A non-sdk 23 implied feature takes precedence.
+    if (entry->second.implied_from_sdk_k23 && !sdk23) {
+      entry->second.implied_from_sdk_k23 = false;
+    }
+
+    entry->second.reasons.insert(reason);
+  }
+
+  /**
+   * Adds a feature to a set of implied features for all features that are implied by the presence
+   * of the permission.
+   **/
+  void addImpliedFeaturesForPermission(int32_t targetSdk, const std::string& name, bool sdk23) {
+    if (name == "android.permission.CAMERA") {
+      addImpliedFeature("android.hardware.camera",
+                        StringPrintf("requested %s permission", name.data()),
+                        sdk23);
+
+    } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
+      if (targetSdk < SDK_LOLLIPOP) {
+        addImpliedFeature("android.hardware.location.gps",
+                          StringPrintf("requested %s permission", name.data()),
+                          sdk23);
+        addImpliedFeature("android.hardware.location.gps",
+                          StringPrintf("targetSdkVersion < %d", SDK_LOLLIPOP),
+                          sdk23);
+      }
+      addImpliedFeature("android.hardware.location",
+                        StringPrintf("requested %s permission", name.data()),
+                        sdk23);
+
+    } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
+      if (targetSdk < SDK_LOLLIPOP) {
+        addImpliedFeature("android.hardware.location.network",
+                          StringPrintf("requested %s permission", name.data()),
+                          sdk23);
+        addImpliedFeature("android.hardware.location.network",
+                          StringPrintf("targetSdkVersion < %d", SDK_LOLLIPOP),
+                          sdk23);
+      }
+      addImpliedFeature("android.hardware.location",
+                        StringPrintf("requested %s permission", name.data()),
+                        sdk23);
+
+    } else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
+        name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
+        name == "android.permission.INSTALL_LOCATION_PROVIDER") {
+      addImpliedFeature("android.hardware.location",
+                        StringPrintf("requested %s permission", name.data()),
+                        sdk23);
+
+    } else if (name == "android.permission.BLUETOOTH" ||
+        name == "android.permission.BLUETOOTH_ADMIN") {
+      if (targetSdk > SDK_DONUT) {
+        addImpliedFeature("android.hardware.bluetooth",
+                          StringPrintf("requested %s permission", name.data()),
+                          sdk23);
+        addImpliedFeature("android.hardware.bluetooth",
+                          StringPrintf("targetSdkVersion > %d", SDK_DONUT),
+                          sdk23);
+      }
+
+    } else if (name == "android.permission.RECORD_AUDIO") {
+      addImpliedFeature("android.hardware.microphone",
+                        StringPrintf("requested %s permission", name.data()),
+                        sdk23);
+
+    } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
+        name == "android.permission.CHANGE_WIFI_STATE" ||
+        name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
+      addImpliedFeature("android.hardware.wifi",
+                        StringPrintf("requested %s permission", name.data()),
+                        sdk23);
+
+    } else if (name == "android.permission.CALL_PHONE" ||
+        name == "android.permission.CALL_PRIVILEGED" ||
+        name == "android.permission.MODIFY_PHONE_STATE" ||
+        name == "android.permission.PROCESS_OUTGOING_CALLS" ||
+        name == "android.permission.READ_SMS" ||
+        name == "android.permission.RECEIVE_SMS" ||
+        name == "android.permission.RECEIVE_MMS" ||
+        name == "android.permission.RECEIVE_WAP_PUSH" ||
+        name == "android.permission.SEND_SMS" ||
+        name == "android.permission.WRITE_APN_SETTINGS" ||
+        name == "android.permission.WRITE_SMS") {
+      addImpliedFeature("android.hardware.telephony",
+                        "requested a telephony permission",
+                        sdk23);
+    }
+  }
+
+ private:
+  /**
+   * Represents a feature that has been automatically added due to a pre-requisite or for some
+   * other reason.
+   */
+  struct ImpliedFeature {
+    explicit ImpliedFeature(bool sdk23 = false) : implied_from_sdk_k23(sdk23) {}
+
+    /** List of human-readable reasons for why this feature was implied. */
+    std::set<std::string> reasons;
+
+    // Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)
+    bool implied_from_sdk_k23;
+  };
+
+  /* Mapping of implied feature names to their properties. */
+  std::map<std::string, ImpliedFeature> implied_features_;
+};
+
+/** Represents <uses-feature> elements. **/
+class UsesFeature : public ManifestExtractor::Element {
+ public:
+  UsesFeature() = default;
+  void Extract(xml::Element* element) override {
+    const std::string* name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+    int32_t* gl = GetAttributeInteger(FindAttribute(element, GL_ES_VERSION_ATTR));
+    bool required = GetAttributeIntegerDefault(
+        FindAttribute(element, REQUIRED_ATTR), true) != 0;
+    int32_t version = GetAttributeIntegerDefault(
+        FindAttribute(element, kAndroidNamespace, "version"), 0);
+
+    // Add the feature to the parent feature group element if one exists; otherwise, add it to the
+    // common feature group
+    FeatureGroup* feature_group = ElementCast<FeatureGroup>(extractor()->parent_stack()[0]);
+    if (!feature_group) {
+      feature_group = extractor()->GetCommonFeatureGroup();
+    } else {
+      // All features in side of <feature-group> elements are required.
+      required = true;
+    }
+
+    if (name) {
+      feature_group->AddFeature(*name, required, version);
+    } else if (gl) {
+      feature_group->open_gles_version = std::max(feature_group->open_gles_version, *gl);
+    }
+  }
+};
+
+/** Represents <uses-permission> elements. **/
+class UsesPermission : public ManifestExtractor::Element {
+ public:
+  UsesPermission() = default;
+  std::string name;
+  std::string requiredFeature;
+  std::string requiredNotFeature;
+  int32_t required = true;
+  int32_t maxSdkVersion = -1;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    requiredFeature = GetAttributeStringDefault(
+        FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
+    requiredNotFeature = GetAttributeStringDefault(
+        FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+    required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+    maxSdkVersion = GetAttributeIntegerDefault(
+        FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
+
+    if (!name.empty()) {
+      CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+      common->addImpliedFeaturesForPermission(extractor()->target_sdk(), name, false);
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    if (!name.empty()) {
+      printer.Print(StringPrintf("uses-permission: name='%s'", name.data()));
+      if (maxSdkVersion >= 0) {
+        printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+      }
+      if (!requiredFeature.empty()) {
+        printer.Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
+      }
+      if (!requiredNotFeature.empty()) {
+        printer.Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
+      }
+      printer.Print("\n");
+      if (required == 0) {
+        printer.Print(StringPrintf("optional-permission: name='%s'", name.data()));
+        if (maxSdkVersion >= 0) {
+          printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+        }
+        printer.Print("\n");
+      }
+    }
+  }
+
+  void PrintImplied(text::Printer& printer, const std::string& reason) {
+    printer.Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
+    if (maxSdkVersion >= 0) {
+      printer.Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+    }
+    printer.Print(StringPrintf(" reason='%s'\n", reason.data()));
+  }
+};
+
+/** Represents <uses-permission-sdk-23> elements. **/
+class UsesPermissionSdk23 : public ManifestExtractor::Element {
+ public:
+  UsesPermissionSdk23() = default;
+  const std::string* name = nullptr;
+  const int32_t* maxSdkVersion = nullptr;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+    maxSdkVersion = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
+
+    if (name) {
+      CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+      common->addImpliedFeaturesForPermission(extractor()->target_sdk(), *name, true);
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    if (name) {
+      printer.Print(StringPrintf("uses-permission-sdk-23: name='%s'", name->data()));
+      if (maxSdkVersion) {
+        printer.Print(StringPrintf(" maxSdkVersion='%d'", *maxSdkVersion));
+      }
+      printer.Print("\n");
+    }
+  }
+};
+
+/** Represents <permission> elements. These elements are only printing when dumping permissions. **/
+class Permission : public ManifestExtractor::Element {
+ public:
+  Permission() = default;
+  std::string name;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+  }
+
+  void Print(text::Printer& printer) override {
+    if (extractor()->options_.only_permissions && !name.empty()) {
+      printer.Print(StringPrintf("permission: %s\n", name.data()));
+    }
+  }
+};
+
+/** Represents <activity> elements. **/
+class Activity : public ManifestExtractor::Element {
+ public:
+  Activity() = default;
+  std::string name;
+  std::string icon;
+  std::string label;
+  std::string banner;
+
+  bool has_component_ = false;
+  bool has_launcher_category = false;
+  bool has_leanback_launcher_category = false;
+  bool has_main_action = false;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    label = GetAttributeStringDefault(FindAttribute(element, LABEL_ATTR), "");
+    icon = GetAttributeStringDefault(FindAttribute(element, ICON_ATTR), "");
+    banner = GetAttributeStringDefault(FindAttribute(element, BANNER_ATTR), "");
+
+    // Retrieve the package name from the manifest
+    std::string package;
+    for (auto& parent : extractor()->parent_stack()) {
+      if (auto manifest = ElementCast<Manifest>(parent)) {
+        package = manifest->package;
+        break;
+      }
+    }
+
+    // Fully qualify the activity name
+    ssize_t idx = name.find(".");
+    if (idx == 0) {
+      name = package + name;
+    } else if (idx < 0) {
+      name = package + "." + name;
+    }
+
+    auto orientation = GetAttributeInteger(FindAttribute(element, SCREEN_ORIENTATION_ATTR));
+    if (orientation) {
+      CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+      int orien = *orientation;
+      if (orien == 0 || orien == 6 || orien == 8) {
+        // Requests landscape, sensorLandscape, or reverseLandscape.
+        common->addImpliedFeature("android.hardware.screen.landscape",
+                                  "one or more activities have specified a landscape orientation",
+                                  false);
+      } else if (orien == 1 || orien == 7 || orien == 9) {
+        // Requests portrait, sensorPortrait, or reversePortrait.
+        common->addImpliedFeature("android.hardware.screen.portrait",
+                                  "one or more activities have specified a portrait orientation",
+                                  false);
+      }
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    // Print whether the activity has the HOME category and a the MAIN action
+    if (has_main_action && has_launcher_category) {
+      printer.Print("launchable-activity:");
+      if (!name.empty()) {
+        printer.Print(StringPrintf(" name='%s' ", name.data()));
+      }
+      printer.Print(StringPrintf(" label='%s' icon='%s'\n",
+                                  android::ResTable::normalizeForOutput(label.data()).c_str(),
+                                  icon.data()));
+    }
+
+    // Print wether the activity has the HOME category and a the MAIN action
+    if (has_leanback_launcher_category) {
+      printer.Print("leanback-launchable-activity:");
+      if (!name.empty()) {
+        printer.Print(StringPrintf(" name='%s' ", name.data()));
+      }
+      printer.Print(StringPrintf(" label='%s' icon='%s' banner='%s'\n",
+                                  android::ResTable::normalizeForOutput(label.data()).c_str(),
+                                  icon.data(), banner.data()));
+    }
+  }
+};
+
+/** Represents <intent-filter> elements. */
+class IntentFilter : public ManifestExtractor::Element {
+ public:
+  IntentFilter() = default;
+};
+
+/** Represents <category> elements. */
+class Category : public ManifestExtractor::Element {
+ public:
+  Category() = default;
+  std::string component = "";
+
+  void Extract(xml::Element* element) override {
+    const std::string* category = GetAttributeString(FindAttribute(element, NAME_ATTR));
+
+    auto parent_stack = extractor()->parent_stack();
+    if (category && ElementCast<IntentFilter>(parent_stack[0])
+        && ElementCast<Activity>(parent_stack[1])) {
+      Activity* activity = ElementCast<Activity>(parent_stack[1]);
+
+      if (*category == "android.intent.category.LAUNCHER") {
+        activity->has_launcher_category = true;
+      } else if (*category == "android.intent.category.LEANBACK_LAUNCHER") {
+        activity->has_leanback_launcher_category = true;
+      } else if (*category == "android.intent.category.HOME") {
+        component = "launcher";
+      }
+    }
+  }
+};
+
+/**
+ * Represents <provider> elements. The elements may have an <intent-filter> which may have <action>
+ * elements nested within.
+ **/
+class Provider : public ManifestExtractor::Element {
+ public:
+  Provider() = default;
+  bool has_required_saf_attributes = false;
+
+  void Extract(xml::Element* element) override {
+    const int32_t* exported = GetAttributeInteger(FindAttribute(element, EXPORTED_ATTR));
+    const int32_t* grant_uri_permissions = GetAttributeInteger(
+        FindAttribute(element, GRANT_URI_PERMISSIONS_ATTR));
+    const std::string* permission = GetAttributeString(
+        FindAttribute(element, PERMISSION_ATTR));
+
+    has_required_saf_attributes = ((exported && *exported != 0)
+        && (grant_uri_permissions && *grant_uri_permissions != 0)
+        && (permission && *permission == "android.permission.MANAGE_DOCUMENTS"));
+  }
+};
+
+/** Represents <receiver> elements. **/
+class Receiver : public ManifestExtractor::Element {
+ public:
+  Receiver() = default;
+  const std::string* permission = nullptr;
+  bool has_component = false;
+
+  void Extract(xml::Element* element) override {
+    permission = GetAttributeString(FindAttribute(element, PERMISSION_ATTR));
+  }
+};
+
+/**Represents <service> elements. **/
+class Service : public ManifestExtractor::Element {
+ public:
+  Service() = default;
+  const std::string* permission = nullptr;
+  bool has_component = false;
+
+  void Extract(xml::Element* element) override {
+    permission = GetAttributeString(FindAttribute(element, PERMISSION_ATTR));
+  }
+};
+
+/** Represents <uses-library> elements. **/
+class UsesLibrary : public ManifestExtractor::Element {
+ public:
+  UsesLibrary() = default;
+  std::string name;
+  int required;
+
+  void Extract(xml::Element* element) override {
+    auto parent_stack = extractor()->parent_stack();
+    if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+      name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+      required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    if (!name.empty()) {
+      printer.Print(StringPrintf("uses-library%s:'%s'\n",
+                                 (required == 0) ? "-not-required" : "", name.data()));
+    }
+  }
+};
+
+/**
+ * Represents <meta-data> elements. These tags are only printed when a flag is passed in to
+ * explicitly enable meta data printing.
+ **/
+class MetaData : public ManifestExtractor::Element {
+ public:
+  MetaData() = default;
+  std::string name;
+  const std::string* value;
+  const int* value_int;
+  const std::string* resource;
+  const int* resource_int;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    value = GetAttributeString(FindAttribute(element, VALUE_ATTR));
+    value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR));
+    resource = GetAttributeString(FindAttribute(element, RESOURCE_ATTR));
+    resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR));
+  }
+
+  void Print(text::Printer& printer) override {
+    if (extractor()->options_.include_meta_data && !name.empty()) {
+      printer.Print(StringPrintf("meta-data: name='%s' ", name.data()));
+      if (value) {
+        printer.Print(StringPrintf("value='%s' ", value->data()));
+      } else if (value_int) {
+        printer.Print(StringPrintf("value='%d' ", *value_int));
+      } else {
+        if (resource) {
+          printer.Print(StringPrintf("resource='%s' ", resource->data()));
+        } else if (resource_int) {
+          printer.Print(StringPrintf("resource='%d' ", *resource_int));
+        }
+      }
+      printer.Print("\n");
+    }
+  }
+};
+
+/**
+ * Represents <action> elements. Detects the presence of certain activity, provider, receiver, and
+ * service components.
+ **/
+class Action : public ManifestExtractor::Element {
+ public:
+  Action() = default;
+  std::string component = "";
+
+  void Extract(xml::Element* element) override {
+    auto parent_stack = extractor()->parent_stack();
+    std::string action = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+
+    if (ElementCast<IntentFilter>(parent_stack[0])) {
+      if (ElementCast<Activity>(parent_stack[1])) {
+        // Detects the presence of a particular type of activity.
+        Activity* activity = ElementCast<Activity>(parent_stack[1]);
+        auto map = std::map<std::string, std::string>({
+            { "android.intent.action.MAIN" , "main" },
+            { "android.intent.action.VIDEO_CAMERA" , "camera" },
+            { "android.intent.action.STILL_IMAGE_CAMERA_SECURE" , "camera-secure" },
+        });
+
+        auto entry = map.find(action);
+        if (entry != map.end()) {
+          component = entry->second;
+          activity->has_component_ = true;
+        }
+
+        if (action == "android.intent.action.MAIN") {
+          activity->has_main_action = true;
+        }
+
+      } else if (ElementCast<Receiver>(parent_stack[1])) {
+        // Detects the presence of a particular type of receiver. If the action requires a
+        // permission, then the receiver element is checked for the permission.
+        Receiver* receiver = ElementCast<Receiver>(parent_stack[1]);
+        auto map = std::map<std::string, std::string>({
+            { "android.appwidget.action.APPWIDGET_UPDATE" , "app-widget" },
+            { "android.app.action.DEVICE_ADMIN_ENABLED" , "device-admin" },
+        });
+
+        auto permissions = std::map<std::string, std::string>({
+            { "android.app.action.DEVICE_ADMIN_ENABLED" , "android.permission.BIND_DEVICE_ADMIN" },
+        });
+
+        auto entry = map.find(action);
+        auto permission = permissions.find(action);
+        if (entry != map.end() && (permission == permissions.end()
+            || (receiver->permission && permission->second == *receiver->permission))) {
+          receiver->has_component = true;
+          component = entry->second;
+        }
+
+      } else if (ElementCast<Service>(parent_stack[1])) {
+        // Detects the presence of a particular type of service. If the action requires a
+        // permission, then the service element is checked for the permission.
+        Service* service = ElementCast<Service>(parent_stack[1]);
+        auto map = std::map<std::string, std::string>({
+            { "android.view.InputMethod" , "ime" },
+            { "android.service.wallpaper.WallpaperService" , "wallpaper" },
+            { "android.accessibilityservice.AccessibilityService" , "accessibility" },
+            { "android.printservice.PrintService" , "print-service" },
+            { "android.nfc.cardemulation.action.HOST_APDU_SERVICE" , "host-apdu" },
+            { "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" , "offhost-apdu" },
+            { "android.service.notification.NotificationListenerService" ,"notification-listener" },
+            { "android.service.dreams.DreamService" , "dream" },
+        });
+
+        auto permissions = std::map<std::string, std::string>({
+            { "android.accessibilityservice.AccessibilityService" ,
+              "android.permission.BIND_ACCESSIBILITY_SERVICE" },
+            { "android.printservice.PrintService" , "android.permission.BIND_PRINT_SERVICE" },
+            { "android.nfc.cardemulation.action.HOST_APDU_SERVICE" ,
+              "android.permission.BIND_NFC_SERVICE" },
+            { "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE" ,
+              "android.permission.BIND_NFC_SERVICE" },
+            { "android.service.notification.NotificationListenerService" ,
+              "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" },
+            { "android.service.dreams.DreamService" , "android.permission.BIND_DREAM_SERVICE" },
+        });
+
+        auto entry = map.find(action);
+        auto permission = permissions.find(action);
+        if (entry != map.end() && (permission == permissions.end()
+            || (service->permission && permission->second == *service->permission))) {
+          service->has_component= true;
+          component = entry->second;
+        }
+
+      } else if (ElementCast<Provider>(parent_stack[1])) {
+        // Detects the presence of a particular type of receiver. If the provider requires a
+        // permission, then the provider element is checked for the permission.
+        // Detect whether this action
+        Provider* provider = ElementCast<Provider>(parent_stack[1]);
+        if (action == "android.content.action.DOCUMENTS_PROVIDER"
+            && provider->has_required_saf_attributes) {
+          component = "document-provider";
+        }
+      }
+    }
+
+    // Represents a searchable interface
+    if (action == "android.intent.action.SEARCH") {
+      component = "search";
+    }
+  }
+};
+
+/**
+ * Represents <supports-input> elements. The element may have <input-type> elements nested within.
+ **/
+class SupportsInput : public ManifestExtractor::Element {
+ public:
+  SupportsInput() = default;
+  std::vector<std::string> inputs;
+
+  void Print(text::Printer& printer) override {
+    const size_t size = inputs.size();
+    if (size > 0) {
+      printer.Print("supports-input: '");
+      for (size_t i = 0; i < size; i++) {
+        printer.Print(StringPrintf("value='%s' ", inputs[i].data()));
+      }
+      printer.Print("\n");
+    }
+  }
+};
+
+/** Represents <input-type> elements. **/
+class InputType : public ManifestExtractor::Element {
+ public:
+  InputType() = default;
+  void Extract(xml::Element* element) override {
+    auto name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+    auto parent_stack = extractor()->parent_stack();
+
+    // Add the input to the set of supported inputs
+    if (name && ElementCast<SupportsInput>(parent_stack[0])) {
+      SupportsInput* supports = ElementCast<SupportsInput>(parent_stack[0]);
+      supports->inputs.push_back(*name);
+    }
+  }
+};
+
+/** Represents <original-package> elements. **/
+class OriginalPackage : public ManifestExtractor::Element {
+ public:
+  OriginalPackage() = default;
+  const std::string* name = nullptr;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+  }
+
+  void Print(text::Printer& printer) override {
+    if (name) {
+      printer.Print(StringPrintf("original-package:'%s'\n", name->data()));
+    }
+  }
+};
+
+/** * Represents <package-verifier> elements. **/
+class PackageVerifier : public ManifestExtractor::Element {
+ public:
+  PackageVerifier() = default;
+  const std::string* name = nullptr;
+  const std::string* public_key = nullptr;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+    public_key = GetAttributeString(FindAttribute(element, PUBLIC_KEY_ATTR));
+  }
+
+  void Print(text::Printer& printer) override {
+    if (name && public_key) {
+      printer.Print(StringPrintf("package-verifier: name='%s' publicKey='%s'\n",
+                                 name->data(), public_key->data()));
+    }
+  }
+};
+
+/** Represents <uses-package> elements. **/
+class UsesPackage : public ManifestExtractor::Element {
+ public:
+  UsesPackage() = default;
+  const std::string* name = nullptr;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+  }
+
+  void Print(text::Printer& printer) override {
+    if (name) {
+      printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+    }
+  }
+};
+
+/** Represents <screen> elements found in <compatible-screens> elements. */
+class Screen : public ManifestExtractor::Element {
+ public:
+  Screen() = default;
+  const int32_t* size = nullptr;
+  const int32_t* density = nullptr;
+
+  void Extract(xml::Element* element) override {
+    size = GetAttributeInteger(FindAttribute(element, SCREEN_SIZE_ATTR));
+    density = GetAttributeInteger(FindAttribute(element, SCREEN_DENSITY_ATTR));
+  }
+};
+
+/**
+ * Represents <compatible-screens> elements. These elements have <screen> elements nested within
+ * that each denote a supported screen size and screen density.
+ **/
+class CompatibleScreens : public ManifestExtractor::Element {
+ public:
+  CompatibleScreens() = default;
+  void Print(text::Printer& printer) override {
+    printer.Print("compatible-screens:");
+
+    bool first = true;
+    ForEachChild(this, [&printer, &first](ManifestExtractor::Element* el){
+      if (auto screen = ElementCast<Screen>(el)) {
+        if (first) {
+          first = false;
+        } else {
+          printer.Print(",");
+        }
+
+        if (screen->size && screen->density) {
+          printer.Print(StringPrintf("'%d/%d'", *screen->size, *screen->density));
+        }
+      }
+    });
+    printer.Print("\n");
+  }
+};
+
+/** Represents <supports-gl-texture> elements. **/
+class SupportsGlTexture : public ManifestExtractor::Element {
+ public:
+  SupportsGlTexture() = default;
+  const std::string* name = nullptr;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+  }
+
+  void Print(text::Printer& printer) override {
+    if (name) {
+      printer.Print(StringPrintf("supports-gl-texture:'%s'\n", name->data()));
+    }
+  }
+};
+
+/** Recursively prints the extracted badging element. */
+static void Print(ManifestExtractor::Element* el, text::Printer& printer) {
+  el->Print(printer);
+  for (auto &child : el->children()) {
+    Print(child.get(), printer);
+  }
+}
+
+bool ManifestExtractor::Dump(text::Printer& printer, IDiagnostics* diag) {
+  // Load the manifest
+  std::unique_ptr<xml::XmlResource> doc = apk_->LoadXml("AndroidManifest.xml", diag);
+  if (doc == nullptr) {
+    diag->Error(DiagMessage() << "failed to find AndroidManifest.xml");
+    return false;
+  }
+
+  xml::Element* element = doc->root.get();
+  if (element->name != "manifest") {
+    diag->Error(DiagMessage() << "manifest does not start with <manifest> tag");
+    return false;
+  }
+
+  // Print only the <uses-permission>, <uses-permission-sdk23>, and <permission> elements if
+  // printing only permission elements is requested
+  if (options_.only_permissions) {
+    std::unique_ptr<ManifestExtractor::Element> manifest_element =
+        ManifestExtractor::Element::Inflate(this, element);
+
+    if (auto manifest = ElementCast<Manifest>(manifest_element.get())) {
+      for (xml::Element* child : element->GetChildElements()) {
+        if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
+            || child->name == "permission") {
+          auto permission_element = ManifestExtractor::Element::Inflate(this, child);
+          manifest->AddChild(permission_element);
+        }
+      }
+
+      printer.Print(StringPrintf("package: %s\n", manifest->package.data()));
+      ForEachChild(manifest, [&printer](ManifestExtractor::Element* el) -> void {
+        el->Print(printer);
+      });
+
+      return true;
+    }
+
+    return false;
+  }
+
+  // Collect information about the resource configurations
+  if (apk_->GetResourceTable()) {
+    for (auto &package : apk_->GetResourceTable()->packages) {
+      for (auto &type : package->types) {
+        for (auto &entry : type->entries) {
+          for (auto &value : entry->values) {
+            std::string locale_str = value->config.GetBcp47LanguageTag();
+
+            // Collect all the unique locales of the apk
+            if (locales_.find(locale_str) == locales_.end()) {
+              ConfigDescription config = ManifestExtractor::DummyConfig();
+              config.setBcp47Locale(locale_str.data());
+              locales_.insert(std::make_pair(locale_str, config));
+            }
+
+            // Collect all the unique density of the apk
+            uint16_t density = (value->config.density == 0) ? (uint16_t) 160
+                                                            : value->config.density;
+            if (densities_.find(density) == densities_.end()) {
+              ConfigDescription config = ManifestExtractor::DummyConfig();
+              config.density = density;
+              densities_.insert(std::make_pair(density, config));
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // Extract badging information
+  auto root = Visit(element);
+
+  // Print the elements in order seen
+  Print(root.get(), printer);
+
+  /** Recursively checks the extracted elements for the specified permission. **/
+  auto FindPermission = [&](ManifestExtractor::Element* root,
+                            const std::string& name) -> ManifestExtractor::Element* {
+    return FindElement(root, [&](ManifestExtractor::Element* el) -> bool {
+      if (UsesPermission* permission = ElementCast<UsesPermission>(el)) {
+        return permission->name == name;
+      }
+      return false;
+    });
+  };
+
+  auto PrintPermission = [&printer](const std::string& name, const std::string& reason,
+                                    int32_t max_sdk_version) -> void {
+    auto permission = util::make_unique<UsesPermission>();
+    permission->name = name;
+    permission->maxSdkVersion = max_sdk_version;
+    permission->Print(printer);
+    permission->PrintImplied(printer, reason);
+  };
+
+  // Implied permissions
+  // Pre-1.6 implicitly granted permission compatibility logic
+  CommonFeatureGroup* common_feature_group = GetCommonFeatureGroup();
+  bool insert_write_external = false;
+  auto write_external_permission = ElementCast<UsesPermission>(
+      FindPermission(root.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
+
+  if (target_sdk() < 4) {
+    if (!write_external_permission) {
+      PrintPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
+      insert_write_external = true;
+    }
+
+    if (!FindPermission(root.get(), "android.permission.READ_PHONE_STATE")) {
+      PrintPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
+    }
+  }
+
+  // If the application has requested WRITE_EXTERNAL_STORAGE, we will
+  // force them to always take READ_EXTERNAL_STORAGE as well.  We always
+  // do this (regardless of target API version) because we can't have
+  // an app with write permission but not read permission.
+  auto read_external = FindPermission(root.get(), "android.permission.READ_EXTERNAL_STORAGE");
+  if (!read_external && (insert_write_external || write_external_permission)) {
+    PrintPermission("android.permission.READ_EXTERNAL_STORAGE",
+                    "requested WRITE_EXTERNAL_STORAGE",
+                    (write_external_permission) ? write_external_permission->maxSdkVersion : -1);
+  }
+
+  // Pre-JellyBean call log permission compatibility.
+  if (target_sdk() < 16) {
+    if (!FindPermission(root.get(), "android.permission.READ_CALL_LOG")
+        && FindPermission(root.get(), "android.permission.READ_CONTACTS")) {
+      PrintPermission("android.permission.READ_CALL_LOG",
+                      "targetSdkVersion < 16 and requested READ_CONTACTS", -1);
+    }
+
+    if (!FindPermission(root.get(), "android.permission.WRITE_CALL_LOG")
+        && FindPermission(root.get(), "android.permission.WRITE_CONTACTS")) {
+      PrintPermission("android.permission.WRITE_CALL_LOG",
+                      "targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
+    }
+  }
+
+  // If the app hasn't declared the touchscreen as a feature requirement (either
+  // directly or implied, required or not), then the faketouch feature is implied.
+  if (!common_feature_group->HasFeature("android.hardware.touchscreen")) {
+    common_feature_group->addImpliedFeature("android.hardware.faketouch",
+                                            "default feature for all apps", false);
+  }
+
+  // Only print the common feature group if no feature group is defined
+  std::vector<FeatureGroup*> feature_groups;
+  ForEachChild(root.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
+    if (auto feature_group = ElementCast<FeatureGroup>(el)) {
+      feature_groups.push_back(feature_group);
+    }
+  });
+
+  if (feature_groups.empty()) {
+    common_feature_group->PrintGroup(printer);
+  } else {
+    // Merge the common feature group into the feature group
+    for (auto& feature_group : feature_groups) {
+      feature_group->open_gles_version  = std::max(feature_group->open_gles_version,
+                                                   common_feature_group->open_gles_version);
+      feature_group->Merge(common_feature_group);
+      feature_group->PrintGroup(printer);
+    }
+  };
+
+  // Collect the component types of the application
+  std::set<std::string> components;
+  ForEachChild(root.get(), [&components](ManifestExtractor::Element* el) -> void {
+    if (ElementCast<Action>(el)) {
+      auto action = ElementCast<Action>(el);
+      if (!action->component.empty()) {
+        components.insert(action->component);
+        return;
+      }
+    }
+
+    if (ElementCast<Category>(el)) {
+      auto category = ElementCast<Category>(el);
+      if (!category->component.empty()) {
+        components.insert(category->component);
+        return;
+      }
+    }
+  });
+
+  // Check for the payment component
+  auto apk = apk_;
+  ForEachChild(root.get(), [&apk, &components, &diag](ManifestExtractor::Element* el) -> void {
+    if (auto service = ElementCast<Service>(el)) {
+      auto host_apdu_action = ElementCast<Action>(FindElement(service,
+        [&](ManifestExtractor::Element* el) -> bool {
+          if (auto action = ElementCast<Action>(el)) {
+            return (action->component == "host-apdu");
+          }
+          return false;
+      }));
+
+      auto offhost_apdu_action = ElementCast<Action>(FindElement(service,
+        [&](ManifestExtractor::Element* el) -> bool {
+           if (auto action = ElementCast<Action>(el)) {
+             return (action->component == "offhost-apdu");
+           }
+           return false;
+      }));
+
+      ForEachChild(service, [&apk, &components, &diag, &host_apdu_action,
+          &offhost_apdu_action](ManifestExtractor::Element* el) -> void {
+        if (auto meta_data = ElementCast<MetaData>(el)) {
+          if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" && host_apdu_action)
+              || (meta_data->name == "android.nfc.cardemulation.off_host_apdu_service"
+                  && offhost_apdu_action)) {
+
+            // Attempt to load the resource file
+            if (!meta_data->resource) {
+              return;
+            }
+            auto resource = apk->LoadXml(*meta_data->resource, diag);
+            if (!resource) {
+              return;
+            }
+
+            // Look for the payment category on an <aid-group> element
+            auto& root = resource.get()->root;
+            if ((host_apdu_action && root->name == "host-apdu-service")
+                || (offhost_apdu_action && root->name == "offhost-apdu-service")) {
+
+              for (auto& child : root->GetChildElements()) {
+                if (child->name == "aid-group") {
+                  auto category = FindAttribute(child, CATEGORY_ATTR);
+                  if (category && category->value == "payment") {
+                    components.insert("payment");
+                    return;
+                  }
+                }
+              }
+            }
+          }
+        }
+      });
+    }
+  });
+
+  // Print the components types if they are present
+  auto PrintComponent = [&components, &printer](const std::string& component) -> void {
+    if (components.find(component) != components.end()) {
+      printer.Print(StringPrintf("provides-component:'%s'\n", component.data()));
+    }
+  };
+
+  PrintComponent("app-widget");
+  PrintComponent("device-admin");
+  PrintComponent("ime");
+  PrintComponent("wallpaper");
+  PrintComponent("accessibility");
+  PrintComponent("print-service");
+  PrintComponent("payment");
+  PrintComponent("search");
+  PrintComponent("document-provider");
+  PrintComponent("launcher");
+  PrintComponent("notification-listener");
+  PrintComponent("dream");
+  PrintComponent("camera");
+  PrintComponent("camera-secure");
+
+  // Print presence of main activity
+  if (components.find("main") != components.end()) {
+    printer.Print("main\n");
+  }
+
+  // Print presence of activities, recivers, and services with no special components
+  FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+    if (auto activity = ElementCast<Activity>(el)) {
+      if (!activity->has_component_) {
+        printer.Print("other-activities\n");
+        return true;
+      }
+    }
+    return false;
+  });
+
+  FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+    if (auto receiver = ElementCast<Receiver>(el)) {
+      if (!receiver->has_component) {
+        printer.Print("other-receivers\n");
+        return true;
+      }
+    }
+    return false;
+  });
+
+  FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
+    if (auto service = ElementCast<Service>(el)) {
+      if (!service->has_component) {
+        printer.Print("other-services\n");
+        return true;
+      }
+    }
+    return false;
+  });
+
+  // Print the supported screens
+  SupportsScreen* screen = ElementCast<SupportsScreen>(FindElement(root.get(),
+      [&](ManifestExtractor::Element* el) -> bool {
+    return ElementCast<SupportsScreen>(el) != nullptr;
+  }));
+
+  if (screen) {
+    screen->PrintScreens(printer, target_sdk_);
+  } else {
+    // Print the default supported screens
+    SupportsScreen default_screens;
+    default_screens.PrintScreens(printer, target_sdk_);
+  }
+
+  // Print all the unique locales of the apk
+  printer.Print("locales:");
+  for (auto& config : locales_) {
+    if (config.first.empty()) {
+      printer.Print(" '--_--'");
+    } else {
+      printer.Print(StringPrintf(" '%s'", config.first.data()));
+    }
+  }
+  printer.Print("\n");
+
+  // Print all the densities locales of the apk
+  printer.Print("densities:");
+  for (auto& config : densities_) {
+    printer.Print(StringPrintf(" '%d'", config.first));
+  }
+  printer.Print("\n");
+
+  // Print the supported architectures of the app
+  std::set<std::string> architectures;
+  auto it = apk_->GetFileCollection()->Iterator();
+  while (it->HasNext()) {
+    auto file_path = it->Next()->GetSource().path;
+
+
+    size_t pos = file_path.find("lib/");
+    if (pos != std::string::npos) {
+      file_path = file_path.substr(pos + 4);
+      pos = file_path.find("/");
+      if (pos != std::string::npos) {
+        file_path = file_path.substr(0, pos);
+      }
+
+      architectures.insert(file_path);
+    }
+  }
+
+  // Determine if the application has multiArch supports
+  auto has_multi_arch = FindElement(root.get(), [&](ManifestExtractor::Element* el) -> bool {
+    if (auto application = ElementCast<Application>(el)) {
+      return application->has_multi_arch;
+    }
+    return false;
+  });
+
+  bool output_alt_native_code = false;
+  // A multiArch package is one that contains 64-bit and
+  // 32-bit versions of native code and expects 3rd-party
+  // apps to load these native code libraries. Since most
+  // 64-bit systems also support 32-bit apps, the apps
+  // loading this multiArch package's code may be either
+  if (has_multi_arch) {
+    // If this is a multiArch package, report the 64-bit
+    // version only. Then as a separate entry, report the
+    // rest.
+    //
+    // If we report the 32-bit architecture, this APK will
+    // be installed on a 32-bit device, causing a large waste
+    // of bandwidth and disk space. This assumes that
+    // the developer of the multiArch package has also
+    // made a version that is 32-bit only.
+    const std::string kIntel64 = "x86_64";
+    const std::string kArm64 = "arm64-v8a";
+
+    auto arch = architectures.find(kIntel64);
+    if (arch == architectures.end()) {
+      arch = architectures.find(kArm64);
+    }
+
+    if (arch != architectures.end()) {
+      printer.Print(StringPrintf("native-code: '%s'\n", arch->data()));
+      architectures.erase(arch);
+      output_alt_native_code = true;
+    }
+  }
+
+  if (architectures.size() > 0) {
+    if (output_alt_native_code) {
+      printer.Print("alt-");
+    }
+    printer.Print("native-code:");
+    for (auto& arch : architectures) {
+      printer.Print(StringPrintf(" '%s'", arch.data()));
+    }
+    printer.Print("\n");
+  }
+
+  return true;
+}
+
+/**
+ * Returns the element casted to the type if the element is of that type. Otherwise, returns a null
+ * pointer.
+ **/
+template<typename T>
+T* ElementCast(ManifestExtractor::Element* element) {
+  if (element == nullptr) {
+    return nullptr;
+  }
+
+  const std::unordered_map<std::string, bool> kTagCheck = {
+    {"action", std::is_base_of<Action, T>::value},
+    {"activity", std::is_base_of<Activity, T>::value},
+    {"application", std::is_base_of<Application, T>::value},
+    {"category", std::is_base_of<Category, T>::value},
+    {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
+    {"feature-group", std::is_base_of<FeatureGroup, T>::value},
+    {"input-type", std::is_base_of<InputType, T>::value},
+    {"intent-filter", std::is_base_of<IntentFilter, T>::value},
+    {"meta-data", std::is_base_of<MetaData, T>::value},
+    {"manifest", std::is_base_of<Manifest, T>::value},
+    {"original-package", std::is_base_of<OriginalPackage, T>::value},
+    {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
+    {"permission", std::is_base_of<Permission, T>::value},
+    {"provider", std::is_base_of<Provider, T>::value},
+    {"receiver", std::is_base_of<Receiver, T>::value},
+    {"screen", std::is_base_of<Screen, T>::value},
+    {"service", std::is_base_of<Service, T>::value},
+    {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
+    {"supports-input", std::is_base_of<SupportsInput, T>::value},
+    {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
+    {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
+    {"uses-feature", std::is_base_of<UsesFeature, T>::value},
+    {"uses-permission", std::is_base_of<UsesPermission, T>::value},
+    {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
+    {"uses-library", std::is_base_of<UsesLibrary, T>::value},
+    {"uses-package", std::is_base_of<UsesPackage, T>::value},
+    {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+  };
+
+  auto check = kTagCheck.find(element->tag());
+  if (check != kTagCheck.end() && check->second) {
+    return static_cast<T*>(element);
+  }
+  return nullptr;
+}
+
+template<typename T>
+std::unique_ptr<T> CreateType() {
+  return std::move(util::make_unique<T>());
+}
+
+std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
+    ManifestExtractor* extractor, xml::Element* el) {
+  const std::unordered_map<std::string,
+                           std::function<std::unique_ptr<ManifestExtractor::Element>()>>
+      kTagCheck = {
+    {"action", &CreateType<Action>},
+    {"activity", &CreateType<Activity>},
+    {"application", &CreateType<Application>},
+    {"category", &CreateType<Category>},
+    {"compatible-screens", &CreateType<CompatibleScreens>},
+    {"feature-group", &CreateType<FeatureGroup>},
+    {"input-type", &CreateType<InputType>},
+    {"intent-filter",&CreateType<IntentFilter>},
+    {"manifest", &CreateType<Manifest>},
+    {"meta-data", &CreateType<MetaData>},
+    {"original-package", &CreateType<OriginalPackage>},
+    {"package-verifier", &CreateType<PackageVerifier>},
+    {"permission", &CreateType<Permission>},
+    {"provider", &CreateType<Provider>},
+    {"receiver", &CreateType<Receiver>},
+    {"screen", &CreateType<Screen>},
+    {"service", &CreateType<Service>},
+    {"supports-gl-texture", &CreateType<SupportsGlTexture>},
+    {"supports-input", &CreateType<SupportsInput>},
+    {"supports-screens", &CreateType<SupportsScreen>},
+    {"uses-configuration", &CreateType<UsesConfiguarion>},
+    {"uses-feature", &CreateType<UsesFeature>},
+    {"uses-permission", &CreateType<UsesPermission>},
+    {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
+    {"uses-library", &CreateType<UsesLibrary>},
+    {"uses-package", &CreateType<UsesPackage>},
+    {"uses-sdk", &CreateType<UsesSdkBadging>},
+  };
+
+  // Attempt to map the xml tag to a element inflater
+  std::unique_ptr<ManifestExtractor::Element> element;
+  auto check = kTagCheck.find(el->name);
+  if (check != kTagCheck.end()) {
+    element = check->second();
+  } else {
+    element = util::make_unique<ManifestExtractor::Element>();
+  }
+
+  element->extractor_ = extractor;
+  element->tag_ = el->name;
+  element->Extract(el);
+  return element;
+}
+
+std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Visit(xml::Element* el) {
+  auto element = ManifestExtractor::Element::Inflate(this, el);
+  parent_stack_.insert(parent_stack_.begin(), element.get());
+
+  // Process the element and recursively visit the children
+  for (xml::Element* child : el->GetChildElements()) {
+    auto v = Visit(child);
+    element->AddChild(v);
+  }
+
+  parent_stack_.erase(parent_stack_.begin());
+  return element;
+}
+
+// Use a smaller buffer so that there is less latency for dumping to stdout.
+constexpr size_t kStdOutBufferSize = 1024u;
+
+int DumpBadgingCommand::Action(const std::vector<std::string>& args) {
+  if (args.size() < 1) {
+    diag_->Error(DiagMessage() << "No dump apk specified.");
+    return 1;
+  }
+
+  ManifestExtractor::Options options;
+  options.include_meta_data = include_metadata_;
+
+  io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+  text::Printer printer(&fout);
+  for (auto apk : args) {
+    auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
+    if (!loaded_apk) {
+      return 1;
+    }
+
+    ManifestExtractor extractor(loaded_apk.get(), options);
+    extractor.Dump(printer, diag_);
+  }
+
+  return 0;
+}
+
+int DumpPermissionsCommand::Action(const std::vector<std::string>& args) {
+  if (args.size() < 1) {
+    diag_->Error(DiagMessage() << "No dump apk specified.");
+    return 1;
+  }
+
+  ManifestExtractor::Options options;
+  options.only_permissions = true;
+
+  io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
+  text::Printer printer(&fout);
+  for (auto apk : args) {
+    auto loaded_apk = LoadedApk::LoadApkFromPath(apk, diag_);
+    if (!loaded_apk) {
+      return 1;
+    }
+
+    ManifestExtractor extractor(loaded_apk.get(), options);
+    extractor.Dump(printer, diag_);
+  }
+
+  return 0;
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/dump/DumpManifest.h b/tools/aapt2/dump/DumpManifest.h
new file mode 100644
index 0000000..a70be53
--- /dev/null
+++ b/tools/aapt2/dump/DumpManifest.h
@@ -0,0 +1,61 @@
+/*
+ * 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 AAPT2_DUMP_MANIFEST_H
+#define AAPT2_DUMP_MANIFEST_H
+
+#include <Diagnostics.h>
+#include <ValueVisitor.h>
+#include <io/ZipArchive.h>
+
+
+#include "cmd/Command.h"
+#include "process/IResourceTableConsumer.h"
+#include "text/Printer.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+class DumpBadgingCommand : public Command {
+ public:
+  explicit DumpBadgingCommand(IDiagnostics* diag) : Command("badging"), diag_(diag) {
+    SetDescription("Print information extracted from the manifest of the APK.");
+    AddOptionalSwitch("--include-meta-data", "Include meta-data information.",
+                      &include_metadata_);
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  IDiagnostics* diag_;
+  bool include_metadata_ = false;
+};
+
+class DumpPermissionsCommand : public Command {
+ public:
+  explicit DumpPermissionsCommand(IDiagnostics* diag) : Command("permissions"), diag_(diag) {
+    SetDescription("Print the permissions extracted from the manifest of the APK.");
+  }
+
+  int Action(const std::vector<std::string>& args) override;
+
+ private:
+  IDiagnostics* diag_;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DUMP_MANIFEST_H
\ No newline at end of file
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index b07fb53..5f978a8 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include "google/protobuf/message_lite.h"
+#include "google/protobuf/io/coded_stream.h"
 
 #include "format/Archive.h"
 #include "io/File.h"
@@ -122,6 +123,23 @@
   io::InputStream* in_;
 };
 
+class ProtoInputStreamReader {
+ public:
+  explicit ProtoInputStreamReader(io::InputStream* in) : in_(in) { }
+
+  /** Deserializes a MessageLite proto from the current position in the input stream.*/
+  template <typename T> bool ReadMessage(T *message_lite) {
+    ZeroCopyInputAdaptor adapter(in_);
+    google::protobuf::io::CodedInputStream coded_stream(&adapter);
+    coded_stream.SetTotalBytesLimit(std::numeric_limits<int32_t>::max(),
+                                    coded_stream.BytesUntilTotalBytesLimit());
+    return message_lite->ParseFromCodedStream(&coded_stream);
+  }
+
+ private:
+  io::InputStream* in_;
+};
+
 }  // namespace io
 }  // namespace aapt
 
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index e991743..b5c33062 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -154,6 +154,12 @@
 bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
   bool error = false;
   for (size_t i = 0; i < split_constraints_.size(); i++) {
+    if (split_constraints_[i].configs.size() == 0) {
+      // For now, treat this as a warning. We may consider aborting processing.
+      context->GetDiagnostics()->Warn(DiagMessage()
+                                       << "no configurations for constraint '"
+                                       << split_constraints_[i].name << "'");
+    }
     for (size_t j = i + 1; j < split_constraints_.size(); j++) {
       for (const ConfigDescription& config : split_constraints_[i].configs) {
         if (split_constraints_[j].configs.find(config) != split_constraints_[j].configs.end()) {
diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h
index 6aec257..ed24bc39 100644
--- a/tools/aapt2/split/TableSplitter.h
+++ b/tools/aapt2/split/TableSplitter.h
@@ -30,6 +30,7 @@
 
 struct SplitConstraints {
   std::set<ConfigDescription> configs;
+  std::string name;
 };
 
 struct TableSplitterOptions {
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index fd5262a..be6e510 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -204,6 +204,112 @@
   configuration::PostProcessingConfiguration config_;
 };
 
+class ConfigDescriptionBuilder {
+ public:
+  ConfigDescriptionBuilder() = default;
+
+  ConfigDescriptionBuilder& setMcc(uint16_t mcc) {
+    config_.mcc = mcc;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setMnc(uint16_t mnc) {
+    config_.mnc = mnc;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setLanguage(uint16_t language) {
+    config_.language[0] = language >> 8;
+    config_.language[1] = language & 0xff;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setCountry(uint16_t country) {
+    config_.country[0] = country >> 8;
+    config_.country[1] = country & 0xff;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setOrientation(uint8_t orientation) {
+    config_.orientation = orientation;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setTouchscreen(uint8_t touchscreen) {
+    config_.touchscreen = touchscreen;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setDensity(uint16_t density) {
+    config_.density = density;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setKeyboard(uint8_t keyboard) {
+    config_.keyboard = keyboard;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setNavigation(uint8_t navigation) {
+    config_.navigation = navigation;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setInputFlags(uint8_t inputFlags) {
+    config_.inputFlags = inputFlags;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setInputPad0(uint8_t inputPad0) {
+    config_.inputPad0 = inputPad0;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenWidth(uint16_t screenWidth) {
+    config_.screenWidth = screenWidth;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenHeight(uint16_t screenHeight) {
+    config_.screenHeight = screenHeight;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setSdkVersion(uint16_t sdkVersion) {
+    config_.sdkVersion = sdkVersion;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setMinorVersion(uint16_t minorVersion) {
+    config_.minorVersion = minorVersion;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenLayout(uint8_t screenLayout) {
+    config_.screenLayout = screenLayout;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setUiMode(uint8_t uiMode) {
+    config_.uiMode = uiMode;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setSmallestScreenWidthDp(uint16_t smallestScreenWidthDp) {
+    config_.smallestScreenWidthDp = smallestScreenWidthDp;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenWidthDp(uint16_t screenWidthDp) {
+    config_.screenWidthDp = screenWidthDp;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenHeightDp(uint16_t screenHeightDp) {
+    config_.screenHeightDp = screenHeightDp;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenLayout2(uint8_t screenLayout2) {
+    config_.screenLayout2 = screenLayout2;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setColorMode(uint8_t colorMode) {
+    config_.colorMode = colorMode;
+    return *this;
+  }
+  ConfigDescriptionBuilder& setScreenConfigPad2(uint16_t screenConfigPad2) {
+    config_.screenConfigPad2 = screenConfigPad2;
+    return *this;
+  }
+  ConfigDescription Build() {
+    return config_;
+  }
+
+ private:
+  ConfigDescription config_;
+};
+
 }  // namespace test
 }  // namespace aapt
 
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index bb02dec..b4dfac6 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -127,7 +127,7 @@
     public static final int OSU_STATUS_SERVICE_PROVIDER_VERIFIED = 5;
 
     /**
-     * The status code for provisioning flow to indicate starting the SOAP exchange.
+     * The status code for provisioning flow to indicate starting the first SOAP exchange.
      */
     public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6;
 
@@ -142,6 +142,11 @@
     public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8;
 
     /**
+     * The status code for provisioning flow to indicate starting the second SOAP exchange.
+     */
+    public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9;
+
+    /**
      * Provisioning status for OSU failure
      *
      * @param status indicates error condition