Merge "use custom Parcel format to pull data"
diff --git a/Android.bp b/Android.bp
index d070fa4..1c59e2b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1230,6 +1230,11 @@
         "metalava-manual",
         "ojluni-annotated-sdk-stubs",
     ],
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
 }
 
 droidstubs {
@@ -1585,6 +1590,7 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -1609,6 +1615,7 @@
             removed_api_file: "api/system-removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
diff --git a/Android.mk b/Android.mk
index 73c0233..d333074 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,262 +53,10 @@
 	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))
diff --git a/api/current.txt b/api/current.txt
index a0ba69d..75787a7 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
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 43b97ab..47b4cfc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -365,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);
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/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 0367d9b..0472461 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2227,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;
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/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index b5b4432..20e1454e 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -136,6 +136,24 @@
     public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider);
 
     /**
+     * Sets the SMS packages provider.
+     * @param provider The packages provider.
+     */
+    public abstract void setSmsAppPackagesProvider(PackagesProvider provider);
+
+    /**
+     * Sets the dialer packages provider.
+     * @param provider The packages provider.
+     */
+    public abstract void setDialerAppPackagesProvider(PackagesProvider provider);
+
+    /**
+     * Sets the sim call manager packages provider.
+     * @param provider The packages provider.
+     */
+    public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider);
+
+    /**
      * Sets the Use Open Wifi packages provider.
      * @param provider The packages provider.
      */
@@ -148,28 +166,26 @@
     public abstract void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider);
 
     /**
-     * Called when the package for the default dialer changed
-     *
-     * @param packageName the new dialer package
-     * @param userId user for which the change was made
+     * Requests granting of the default permissions to the current default SMS app.
+     * @param packageName The default SMS package name.
+     * @param userId The user for which to grant the permissions.
      */
-    public void onDefaultDialerAppChanged(String packageName, int userId) {}
+    public abstract void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId);
 
     /**
-     * Called when the package for the default SMS handler changed
-     *
-     * @param packageName the new sms package
-     * @param userId user for which the change was made
+     * Requests granting of the default permissions to the current default dialer app.
+     * @param packageName The default dialer package name.
+     * @param userId The user for which to grant the permissions.
      */
-    public void onDefaultSmsAppChanged(String packageName, int userId) {}
+    public abstract void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId);
 
     /**
-     * Called when the package for the default sim call manager changed
-     *
-     * @param packageName the new sms package
-     * @param userId user for which the change was made
+     * Requests granting of the default permissions to the current default sim call manager.
+     * @param packageName The default sim call manager package name.
+     * @param userId The user for which to grant the permissions.
      */
-    public void onDefaultSimCallManagerAppChanged(String packageName, int userId) {}
+    public abstract void grantDefaultPermissionsToDefaultSimCallManager(String packageName,
+            int userId);
 
     /**
      * Requests granting of the default permissions to the current default Use Open Wifi app.
@@ -430,8 +446,8 @@
          *
          * @param packageName The package to check for
          * @param uid the uid in which the package is running
-         * @return {@link #USER_TRUSTED} if the user has trusted the package, {@link #USER_BLOCKED}
-         * if user has blocked requests from the package, {@link #USER_DEFAULT} if the user response
+         * @return {@link USER_TRUSTED} if the user has trusted the package, {@link USER_BLOCKED}
+         * if user has blocked requests from the package, {@link USER_DEFAULT} if the user response
          * is not yet available
          */
         int getPackageTrustedToInstallApps(String packageName, int uid);
@@ -545,7 +561,7 @@
     /**
      * Returns a list without a change observer.
      *
-     * @see #getPackageList(PackageListObserver)
+     * {@see #getPackageList(PackageListObserver)}
      */
     public @NonNull PackageList getPackageList() {
         return getPackageList(null);
@@ -574,16 +590,7 @@
     /**
      * Returns a package object for the disabled system package name.
      */
-    public abstract @Nullable PackageParser.Package getDisabledSystemPackage(
-            @NonNull String packageName);
-
-    /**
-     * Returns the package name for the disabled system package.
-     *
-     * This is equivalent to
-     * {@link #getDisabledSystemPackage(String)}.{@link PackageParser.Package#packageName}
-     */
-    public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
+    public abstract @Nullable PackageParser.Package getDisabledPackage(@NonNull String packageName);
 
     /**
      * Returns whether or not the component is the resolver activity.
@@ -612,7 +619,7 @@
      * Access may be limited based upon whether the calling or target applications
      * are instant applications.
      *
-     * @see #canAccessInstantApps
+     * @see #canAccessInstantApps(int)
      */
     public abstract boolean filterAppAccess(
             @Nullable PackageParser.Package pkg, int callingUid, int userId);
@@ -628,9 +635,6 @@
     public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
             @NonNull String packageName, int flagMask, int flagValues, int userId);
 
-    /** Returns whether the given package was signed by the platform */
-    public abstract boolean isPlatformSigned(String pkg);
-
     /**
      * Returns true if it's still safe to restore data backed up from this app's version
      * that was signed with restoringFromSigHash.
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/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/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/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/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 508509a..2f677f9 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 */
@@ -1790,7 +1788,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 +1797,7 @@
             }
             return;
         }
-        mPrivOpsRegistry.get(token).setInputMethod(id);
+        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
     }
 
     /**
@@ -1819,7 +1817,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 +1826,7 @@
             }
             return;
         }
-        mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
+        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
     }
 
     /**
@@ -1848,7 +1846,7 @@
      */
     @Deprecated
     public void hideSoftInputFromInputMethod(IBinder token, int flags) {
-        mPrivOpsRegistry.get(token).hideMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
     }
 
     /**
@@ -1869,7 +1867,7 @@
      */
     @Deprecated
     public void showSoftInputFromInputMethod(IBinder token, int flags) {
-        mPrivOpsRegistry.get(token).showMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
     }
 
     /**
@@ -2229,7 +2227,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 +2235,7 @@
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
     }
 
     /**
@@ -2257,7 +2255,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 +2263,8 @@
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+                .switchToNextInputMethod(onlyCurrentIme);
     }
 
     /**
@@ -2284,7 +2283,8 @@
      */
     @Deprecated
     public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
-        return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+                .shouldOfferSwitchingToNextInputMethod();
     }
 
     /**
@@ -2420,34 +2420,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/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/TextView.java b/core/java/android/widget/TextView.java
index a86e6f8..dfbaf9a 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);
     }
 
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/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/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/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/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/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 8ae8525..165fc48 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -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/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..3c281e78 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/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/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/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/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/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/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/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..a725a0b 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;
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..459b4d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -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();
         }
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/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 d019b6b..73c4223 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -219,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
@@ -1112,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 0a2e588..9f4fff8 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -55,6 +55,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.AuthenticationClient;
@@ -104,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.
      */
@@ -151,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);
         }
@@ -166,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);
         }
@@ -373,7 +401,8 @@
                 throws RemoteException {
             if (mBiometricPromptServiceReceiver != null) {
                 mBiometricPromptServiceReceiver.onAcquired(deviceId, acquiredInfo,
-                        mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+                        FingerprintManager.getAcquiredString(
+                            getContext(), acquiredInfo, vendorCode));
             }
         }
 
@@ -396,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));
             }
         }
     }
@@ -569,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.
@@ -740,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/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..560ca92 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -378,7 +378,7 @@
         for (int i = newIntents.size() - 1; i >= 0; --i) {
             final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i);
             final PackageParser.Package disabledPkg = sPackageManagerInternal
-                    .getDisabledSystemPackage(intentInfo.activity.info.packageName);
+                    .getDisabledPackage(intentInfo.activity.info.packageName);
             final List<PackageParser.Activity> systemActivities =
                     disabledPkg != null ? disabledPkg.activities : null;
             adjustPriority(systemActivities, intentInfo, setupWizardPackage);
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 5c6fe16..5810e30 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -53,10 +53,6 @@
     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.
@@ -288,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");
 
@@ -338,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 */,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 91af0ec..db0c13c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -718,8 +718,6 @@
     @GuardedBy("mPackages")
     final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
 
-    private PackageManager mPackageManager;
-
     class PackageParserCallback implements PackageParser.Callback {
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -22238,22 +22236,6 @@
         }
 
         @Override
-        public boolean isPlatformSigned(String packageName) {
-            PackageSetting packageSetting = mSettings.mPackages.get(packageName);
-            if (packageSetting == null) {
-                return false;
-            }
-            PackageParser.Package pkg = packageSetting.pkg;
-            if (pkg == null) {
-                // May happen if package in on a removable sd card
-                return false;
-            }
-            return pkg.mSigningDetails.hasAncestorOrSelf(mPlatformPackage.mSigningDetails)
-                    || mPlatformPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
-                    PackageParser.SigningDetails.CertCapabilities.PERMISSION);
-        }
-
-        @Override
         public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
             SigningDetails sd = getSigningDetails(packageName);
             if (sd == null) {
@@ -22365,7 +22347,7 @@
         }
 
         @Override
-        public PackageParser.Package getDisabledSystemPackage(String packageName) {
+        public PackageParser.Package getDisabledPackage(String packageName) {
             synchronized (mPackages) {
                 final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
                 return (ps != null) ? ps.pkg : null;
@@ -22373,12 +22355,6 @@
         }
 
         @Override
-        public @Nullable String getDisabledSystemPackageName(@NonNull String packageName) {
-            PackageParser.Package pkg = getDisabledSystemPackage(packageName);
-            return pkg == null ? null : pkg.packageName;
-        }
-
-        @Override
         public String getKnownPackageName(int knownPackage, int userId) {
             switch(knownPackage) {
                 case PackageManagerInternal.PACKAGE_BROWSER:
@@ -22416,6 +22392,21 @@
         }
 
         @Override
+        public void setSmsAppPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setSmsAppPackagesProvider(provider);
+        }
+
+        @Override
+        public void setDialerAppPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setDialerAppPackagesProvider(provider);
+        }
+
+        @Override
+        public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setSimCallManagerPackagesProvider(provider);
+        }
+
+        @Override
         public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
             mDefaultPermissionPolicy.setUseOpenWifiAppPackagesProvider(provider);
         }
@@ -22426,10 +22417,22 @@
         }
 
         @Override
-        public void onDefaultDialerAppChanged(String packageName, int userId) {
+        public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsApp(packageName, userId);
+        }
+
+        @Override
+        public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
             synchronized (mPackages) {
                 mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
             }
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerApp(packageName, userId);
+        }
+
+        @Override
+        public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+                    packageName, userId);
         }
 
         @Override
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..846c7b7 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -23,13 +23,12 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.DownloadManager;
-import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
 import android.companion.CompanionDeviceManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
+import android.content.pm.PackageList;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackagesProvider;
@@ -54,7 +53,6 @@
 import android.provider.MediaStore;
 import android.provider.Telephony.Sms.Intents;
 import android.security.Credentials;
-import android.speech.RecognitionService;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -63,7 +61,6 @@
 import android.util.Slog;
 import android.util.Xml;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
 
@@ -76,7 +73,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -98,15 +94,10 @@
     private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
     private static final boolean DEBUG = false;
 
-    @PackageManager.ResolveInfoFlags
-    private static final int DEFAULT_INTENT_QUERY_FLAGS =
+    private static final int DEFAULT_FLAGS =
             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                     | PackageManager.MATCH_UNINSTALLED_PACKAGES;
 
-    @PackageManager.PackageInfoFlags
-    private static final int DEFAULT_PACKAGE_INFO_QUERY_FLAGS =
-            PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS;
-
     private static final String AUDIO_MIME_TYPE = "audio/mpeg";
 
     private static final String TAG_EXCEPTIONS = "exceptions";
@@ -117,8 +108,6 @@
     private static final String ATTR_FIXED = "fixed";
 
     private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
-
-
     static {
         PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
         PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
@@ -230,7 +219,7 @@
     private final DefaultPermissionGrantedCallback mPermissionGrantedCallback;
     public interface DefaultPermissionGrantedCallback {
         /** Callback when permissions have been granted */
-        void onDefaultRuntimePermissionsGranted(int userId);
+        public void onDefaultRuntimePermissionsGranted(int userId);
     }
 
     public DefaultPermissionGrantPolicy(Context context, Looper looper,
@@ -302,9 +291,9 @@
         grantDefaultPermissionExceptions(userId);
     }
 
-    private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) {
+    private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
         Set<String> permissions = new ArraySet<>();
-        for (String permission : pkg.requestedPermissions) {
+        for (String permission :  pkg.requestedPermissions) {
             final BasePermission bp = mPermissionManager.getPermission(permission);
             if (bp == null) {
                 continue;
@@ -318,71 +307,36 @@
         }
     }
 
+    private void grantAllRuntimePermissions(int userId) {
+        Log.i(TAG, "Granting all runtime permissions for user " + userId);
+        final PackageList packageList = mServiceInternal.getPackageList();
+        for (String packageName : packageList.getPackageNames()) {
+            final PackageParser.Package pkg = mServiceInternal.getPackage(packageName);
+            if (pkg == null) {
+                continue;
+            }
+            grantRuntimePermissionsForPackage(userId, pkg);
+        }
+    }
+
     public void scheduleReadDefaultPermissionExceptions() {
         mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
     }
 
     private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
         Log.i(TAG, "Granting permissions to platform components for user " + userId);
-        List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
-                DEFAULT_PACKAGE_INFO_QUERY_FLAGS, UserHandle.USER_SYSTEM);
-        for (PackageInfo pkg : packages) {
+        final PackageList packageList = mServiceInternal.getPackageList();
+        for (String packageName : packageList.getPackageNames()) {
+            final PackageParser.Package pkg = mServiceInternal.getPackage(packageName);
             if (pkg == null) {
                 continue;
             }
             if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
                     || !doesPackageSupportRuntimePermissions(pkg)
-                    || ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+                    || pkg.requestedPermissions.isEmpty()) {
                 continue;
             }
-            grantRuntimePermissionsForSystemPackage(userId, pkg);
-        }
-    }
-
-    @SafeVarargs
-    private final void grantIgnoringSystemPackage(String packageName, int userId,
-            Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(packageName, userId, false, true, permissionGroups);
-    }
-
-    @SafeVarargs
-    private final void grantSystemFixedPermissionsToSystemPackage(String packageName, int userId,
-            Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(packageName, userId, true, false, permissionGroups);
-    }
-
-    @SafeVarargs
-    private final void grantPermissionsToSystemPackage(
-            String packageName, int userId, Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(packageName, userId, false, false, permissionGroups);
-    }
-
-    @SafeVarargs
-    private final void grantPermissionsToSystemPackage(String packageName, int userId,
-            boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
-        if (!ignoreSystemPackage && !isSystemPackage(packageName)) {
-            return;
-        }
-        grantRuntimePermissionsToPackage(getSystemPackageInfo(packageName),
-                userId, systemFixed, ignoreSystemPackage, permissionGroups);
-    }
-
-    @SafeVarargs
-    private final void grantRuntimePermissionsToPackage(String packageName, int userId,
-            boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
-        grantRuntimePermissionsToPackage(getPackageInfo(packageName),
-                userId, systemFixed, ignoreSystemPackage, permissionGroups);
-    }
-
-    @SafeVarargs
-    private final void grantRuntimePermissionsToPackage(PackageInfo packageName, int userId,
-            boolean systemFixed, boolean ignoreSystemPackage, Set<String>... permissionGroups) {
-        if (packageName == null) return;
-        if (doesPackageSupportRuntimePermissions(packageName)) {
-            for (Set<String> permissionGroup : permissionGroups) {
-                grantRuntimePermissions(packageName, permissionGroup, systemFixed,
-                        ignoreSystemPackage, userId);
-            }
+            grantRuntimePermissionsForPackage(userId, pkg);
         }
     }
 
@@ -425,373 +379,594 @@
                 syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
 
         // Installer
-        grantSystemFixedPermissionsToSystemPackage(
-                getKnownPackage(PackageManagerInternal.PACKAGE_INSTALLER, userId),
-                userId, STORAGE_PERMISSIONS);
+        final String installerPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_INSTALLER, userId);
+        PackageParser.Package installerPackage = getSystemPackage(installerPackageName);
+        if (installerPackage != null
+                && doesPackageSupportRuntimePermissions(installerPackage)) {
+            grantRuntimePermissions(installerPackage, STORAGE_PERMISSIONS, true, userId);
+        }
 
         // Verifier
-        final String verifier = getKnownPackage(PackageManagerInternal.PACKAGE_VERIFIER, userId);
-        grantSystemFixedPermissionsToSystemPackage(verifier, userId, STORAGE_PERMISSIONS);
-        grantPermissionsToSystemPackage(verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
+        final String verifierPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_VERIFIER, userId);
+        PackageParser.Package verifierPackage = getSystemPackage(verifierPackageName);
+        if (verifierPackage != null
+                && doesPackageSupportRuntimePermissions(verifierPackage)) {
+            grantRuntimePermissions(verifierPackage, STORAGE_PERMISSIONS, true, userId);
+            grantRuntimePermissions(verifierPackage, PHONE_PERMISSIONS, false, userId);
+            grantRuntimePermissions(verifierPackage, SMS_PERMISSIONS, false, userId);
+        }
 
         // SetupWizard
-        grantPermissionsToSystemPackage(
-                getKnownPackage(PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId), userId,
-                PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+        final String setupWizardPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId);
+        PackageParser.Package setupPackage = getSystemPackage(setupWizardPackageName);
+        if (setupPackage != null
+                && doesPackageSupportRuntimePermissions(setupPackage)) {
+            grantRuntimePermissions(setupPackage, PHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(setupPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(setupPackage, LOCATION_PERMISSIONS, userId);
+            grantRuntimePermissions(setupPackage, CAMERA_PERMISSIONS, userId);
+        }
 
         // Camera
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(MediaStore.ACTION_IMAGE_CAPTURE, userId),
-                userId, CAMERA_PERMISSIONS, MICROPHONE_PERMISSIONS, STORAGE_PERMISSIONS);
+        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackage(
+                cameraIntent, userId);
+        if (cameraPackage != null
+                && doesPackageSupportRuntimePermissions(cameraPackage)) {
+            grantRuntimePermissions(cameraPackage, CAMERA_PERMISSIONS, userId);
+            grantRuntimePermissions(cameraPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(cameraPackage, STORAGE_PERMISSIONS, userId);
+        }
 
         // Media provider
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId,
-                STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS, MEDIA_VISUAL_PERMISSIONS,
-                PHONE_PERMISSIONS);
+        PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackage(
+                MediaStore.AUTHORITY, userId);
+        if (mediaStorePackage != null) {
+            grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
+            grantRuntimePermissions(mediaStorePackage, MEDIA_AURAL_PERMISSIONS, true, userId);
+            grantRuntimePermissions(mediaStorePackage, MEDIA_VISUAL_PERMISSIONS, true, userId);
+            grantRuntimePermissions(mediaStorePackage, PHONE_PERMISSIONS, true, userId);
+        }
 
         // Downloads provider
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultProviderAuthorityPackage("downloads", userId), userId,
-                STORAGE_PERMISSIONS);
+        PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackage(
+                "downloads", userId);
+        if (downloadsPackage != null) {
+            grantRuntimePermissions(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
+        }
 
         // Downloads UI
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
-                        DownloadManager.ACTION_VIEW_DOWNLOADS, userId),
-                userId, STORAGE_PERMISSIONS);
+        Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+        PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackage(
+                downloadsUiIntent, userId);
+        if (downloadsUiPackage != null
+                && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
+            grantRuntimePermissions(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
+        }
 
         // Storage provider
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultProviderAuthorityPackage("com.android.externalstorage.documents", userId),
-                userId, STORAGE_PERMISSIONS);
+        PackageParser.Package storagePackage = getDefaultProviderAuthorityPackage(
+                "com.android.externalstorage.documents", userId);
+        if (storagePackage != null) {
+            grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId);
+        }
 
         // CertInstaller
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(Credentials.INSTALL_ACTION, userId), userId,
-                STORAGE_PERMISSIONS);
+        Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
+        PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage(
+                certInstallerIntent, userId);
+        if (certInstallerPackage != null
+                && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
+            grantRuntimePermissions(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
+        }
 
         // Dialer
         if (dialerAppPackageNames == null) {
-            String dialerPackage =
-                    getDefaultSystemHandlerActivityPackage(Intent.ACTION_DIAL, userId);
-            grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+            Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
+            PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackage(
+                    dialerIntent, userId);
+            if (dialerPackage != null) {
+                grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+            }
         } else {
             for (String dialerAppPackageName : dialerAppPackageNames) {
-                grantDefaultPermissionsToDefaultSystemDialerApp(dialerAppPackageName, userId);
+                PackageParser.Package dialerPackage = getSystemPackage(dialerAppPackageName);
+                if (dialerPackage != null) {
+                    grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+                }
             }
         }
 
         // Sim call manager
         if (simCallManagerPackageNames != null) {
             for (String simCallManagerPackageName : simCallManagerPackageNames) {
-                grantDefaultPermissionsToDefaultSystemSimCallManager(
-                        simCallManagerPackageName, userId);
+                PackageParser.Package simCallManagerPackage =
+                        getSystemPackage(simCallManagerPackageName);
+                if (simCallManagerPackage != null) {
+                    grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage,
+                            userId);
+                }
             }
         }
 
         // Use Open Wifi
         if (useOpenWifiAppPackageNames != null) {
             for (String useOpenWifiPackageName : useOpenWifiAppPackageNames) {
-                grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
-                        useOpenWifiPackageName, userId);
+                PackageParser.Package useOpenWifiPackage =
+                        getSystemPackage(useOpenWifiPackageName);
+                if (useOpenWifiPackage != null) {
+                    grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(useOpenWifiPackage,
+                            userId);
+                }
             }
         }
 
         // SMS
         if (smsAppPackageNames == null) {
-            String smsPackage = getDefaultSystemHandlerActivityPackageForCategory(
-                    Intent.CATEGORY_APP_MESSAGING, userId);
-            grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+            Intent smsIntent = new Intent(Intent.ACTION_MAIN);
+            smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
+            PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackage(
+                    smsIntent, userId);
+            if (smsPackage != null) {
+               grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+            }
         } else {
-            for (String smsPackage : smsAppPackageNames) {
-                grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+            for (String smsPackageName : smsAppPackageNames) {
+                PackageParser.Package smsPackage = getSystemPackage(smsPackageName);
+                if (smsPackage != null) {
+                    grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+                }
             }
         }
 
         // Cell Broadcast Receiver
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(Intents.SMS_CB_RECEIVED_ACTION, userId),
-                userId, SMS_PERMISSIONS);
+        Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
+        PackageParser.Package cbrPackage =
+                getDefaultSystemHandlerActivityPackage(cbrIntent, userId);
+        if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
+            grantRuntimePermissions(cbrPackage, SMS_PERMISSIONS, userId);
+        }
 
         // Carrier Provisioning Service
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerServicePackage(Intents.SMS_CARRIER_PROVISION_ACTION, userId),
-                userId, SMS_PERMISSIONS);
+        Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
+        PackageParser.Package carrierProvPackage =
+                getDefaultSystemHandlerServicePackage(carrierProvIntent, userId);
+        if (carrierProvPackage != null
+                && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
+            grantRuntimePermissions(carrierProvPackage, SMS_PERMISSIONS, false, userId);
+        }
 
         // Calendar
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
-                        Intent.CATEGORY_APP_CALENDAR, userId),
-                userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS);
+        Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
+        calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
+        PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackage(
+                calendarIntent, userId);
+        if (calendarPackage != null
+                && doesPackageSupportRuntimePermissions(calendarPackage)) {
+            grantRuntimePermissions(calendarPackage, CALENDAR_PERMISSIONS, userId);
+            grantRuntimePermissions(calendarPackage, CONTACTS_PERMISSIONS, userId);
+        }
 
         // Calendar provider
-        String calendarProvider =
-                getDefaultProviderAuthorityPackage(CalendarContract.AUTHORITY, userId);
-        grantPermissionsToSystemPackage(calendarProvider, userId,
-                CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
-        grantSystemFixedPermissionsToSystemPackage(calendarProvider, userId, CALENDAR_PERMISSIONS);
+        PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackage(
+                CalendarContract.AUTHORITY, userId);
+        if (calendarProviderPackage != null) {
+            grantRuntimePermissions(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(calendarProviderPackage, CALENDAR_PERMISSIONS,
+                    true, userId);
+            grantRuntimePermissions(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
+        }
 
         // Calendar provider sync adapters
-        grantPermissionToEachSystemPackage(
-                getHeadlessSyncAdapterPackages(calendarSyncAdapterPackages, userId),
-                userId, CALENDAR_PERMISSIONS);
-
-        // Contacts
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
-                        Intent.CATEGORY_APP_CONTACTS, userId),
-                userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
-
-        // Contacts provider sync adapters
-        grantPermissionToEachSystemPackage(
-                getHeadlessSyncAdapterPackages(contactsSyncAdapterPackages, userId),
-                userId, CONTACTS_PERMISSIONS);
-
-        // Contacts provider
-        String contactsProviderPackage =
-                getDefaultProviderAuthorityPackage(ContactsContract.AUTHORITY, userId);
-        grantSystemFixedPermissionsToSystemPackage(contactsProviderPackage, userId,
-                CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
-        grantPermissionsToSystemPackage(contactsProviderPackage, userId, STORAGE_PERMISSIONS);
-
-        // Device provisioning
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
-                        DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
-                userId, CONTACTS_PERMISSIONS);
-
-        // Maps
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId),
-                userId, LOCATION_PERMISSIONS);
-
-        // Gallery
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
-                        Intent.CATEGORY_APP_GALLERY, userId),
-                userId, STORAGE_PERMISSIONS, MEDIA_VISUAL_PERMISSIONS);
-
-        // Email
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
-                        Intent.CATEGORY_APP_EMAIL, userId),
-                userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
-
-        // Browser
-        String browserPackage = getKnownPackage(PackageManagerInternal.PACKAGE_BROWSER, userId);
-        if (browserPackage == null) {
-            browserPackage = getDefaultSystemHandlerActivityPackageForCategory(
-                    Intent.CATEGORY_APP_BROWSER, userId);
-            if (!isSystemPackage(browserPackage)) {
-                browserPackage = null;
+        List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackages(
+                calendarSyncAdapterPackages, userId);
+        final int calendarSyncAdapterCount = calendarSyncAdapters.size();
+        for (int i = 0; i < calendarSyncAdapterCount; i++) {
+            PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
+            if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
+                grantRuntimePermissions(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
             }
         }
-        grantRuntimePermissionsToPackage(browserPackage, userId,
-                false /* systemFixed */, false /* ignoreSystemPackage */,
-                LOCATION_PERMISSIONS);
+
+        // Contacts
+        Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
+        contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
+        PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackage(
+                contactsIntent, userId);
+        if (contactsPackage != null
+                && doesPackageSupportRuntimePermissions(contactsPackage)) {
+            grantRuntimePermissions(contactsPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(contactsPackage, PHONE_PERMISSIONS, userId);
+        }
+
+        // Contacts provider sync adapters
+        List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackages(
+                contactsSyncAdapterPackages, userId);
+        final int contactsSyncAdapterCount = contactsSyncAdapters.size();
+        for (int i = 0; i < contactsSyncAdapterCount; i++) {
+            PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
+            if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
+                grantRuntimePermissions(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
+            }
+        }
+
+        // Contacts provider
+        PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackage(
+                ContactsContract.AUTHORITY, userId);
+        if (contactsProviderPackage != null) {
+            grantRuntimePermissions(contactsProviderPackage, CONTACTS_PERMISSIONS,
+                    true, userId);
+            grantRuntimePermissions(contactsProviderPackage, PHONE_PERMISSIONS,
+                    true, userId);
+            grantRuntimePermissions(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
+        }
+
+        // Device provisioning
+        Intent deviceProvisionIntent = new Intent(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
+        PackageParser.Package deviceProvisionPackage =
+                getDefaultSystemHandlerActivityPackage(deviceProvisionIntent, userId);
+        if (deviceProvisionPackage != null
+                && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
+            grantRuntimePermissions(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
+        }
+
+        // Maps
+        Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
+        mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
+        PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackage(
+                mapsIntent, userId);
+        if (mapsPackage != null
+                && doesPackageSupportRuntimePermissions(mapsPackage)) {
+            grantRuntimePermissions(mapsPackage, LOCATION_PERMISSIONS, userId);
+        }
+
+        // Gallery
+        Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
+        galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
+        PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackage(
+                galleryIntent, userId);
+        if (galleryPackage != null
+                && doesPackageSupportRuntimePermissions(galleryPackage)) {
+            grantRuntimePermissions(galleryPackage, STORAGE_PERMISSIONS, userId);
+            grantRuntimePermissions(galleryPackage, MEDIA_VISUAL_PERMISSIONS, userId);
+        }
+
+        // Email
+        Intent emailIntent = new Intent(Intent.ACTION_MAIN);
+        emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
+        PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackage(
+                emailIntent, userId);
+        if (emailPackage != null
+                && doesPackageSupportRuntimePermissions(emailPackage)) {
+            grantRuntimePermissions(emailPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(emailPackage, CALENDAR_PERMISSIONS, userId);
+        }
+
+        // Browser
+        PackageParser.Package browserPackage = null;
+        String defaultBrowserPackage = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_BROWSER, userId);
+        if (defaultBrowserPackage != null) {
+            browserPackage = getPackage(defaultBrowserPackage);
+        }
+        if (browserPackage == null) {
+            Intent browserIntent = new Intent(Intent.ACTION_MAIN);
+            browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
+            browserPackage = getDefaultSystemHandlerActivityPackage(
+                    browserIntent, userId);
+        }
+        if (browserPackage != null
+                && doesPackageSupportRuntimePermissions(browserPackage)) {
+            grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId);
+        }
 
         // Voice interaction
         if (voiceInteractPackageNames != null) {
             for (String voiceInteractPackageName : voiceInteractPackageNames) {
-                grantPermissionsToSystemPackage(voiceInteractPackageName, userId,
-                        CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
-                        PHONE_PERMISSIONS, SMS_PERMISSIONS, LOCATION_PERMISSIONS);
+                PackageParser.Package voiceInteractPackage = getSystemPackage(
+                        voiceInteractPackageName);
+                if (voiceInteractPackage != null
+                        && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
+                    grantRuntimePermissions(voiceInteractPackage,
+                            CONTACTS_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            CALENDAR_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            MICROPHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            PHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            SMS_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            LOCATION_PERMISSIONS, userId);
+                }
             }
         }
 
         if (ActivityManager.isLowRamDeviceStatic()) {
             // Allow voice search on low-ram devices
-            grantPermissionsToSystemPackage(
-                    getDefaultSystemHandlerActivityPackage(
-                            SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
-                    userId, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS);
+            Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
+            PackageParser.Package globalSearchPickerPackage =
+                getDefaultSystemHandlerActivityPackage(globalSearchIntent, userId);
+
+            if (globalSearchPickerPackage != null
+                    && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
+                grantRuntimePermissions(globalSearchPickerPackage,
+                    MICROPHONE_PERMISSIONS, false, userId);
+                grantRuntimePermissions(globalSearchPickerPackage,
+                    LOCATION_PERMISSIONS, false, userId);
+            }
         }
 
         // Voice recognition
-        Intent voiceRecoIntent = new Intent(RecognitionService.SERVICE_INTERFACE)
-                .addCategory(Intent.CATEGORY_DEFAULT);
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerServicePackage(voiceRecoIntent, userId), userId,
-                MICROPHONE_PERMISSIONS);
+        Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
+        voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
+        PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackage(
+                voiceRecoIntent, userId);
+        if (voiceRecoPackage != null
+                && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
+            grantRuntimePermissions(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
+        }
 
         // Location
         if (locationPackageNames != null) {
             for (String packageName : locationPackageNames) {
-                grantPermissionsToSystemPackage(packageName, userId,
-                        CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
-                        PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
-                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
-                grantSystemFixedPermissionsToSystemPackage(packageName, userId,
-                        LOCATION_PERMISSIONS);
+                PackageParser.Package locationPackage = getSystemPackage(packageName);
+                if (locationPackage != null
+                        && doesPackageSupportRuntimePermissions(locationPackage)) {
+                    grantRuntimePermissions(locationPackage, CONTACTS_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, CALENDAR_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, MICROPHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, PHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, SMS_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, LOCATION_PERMISSIONS,
+                            true, userId);
+                    grantRuntimePermissions(locationPackage, CAMERA_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, SENSORS_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, STORAGE_PERMISSIONS, userId);
+                }
             }
         }
 
         // Music
-        Intent musicIntent = new Intent(Intent.ACTION_VIEW)
-                .addCategory(Intent.CATEGORY_DEFAULT)
-                .setDataAndType(Uri.fromFile(new File("foo.mp3")), AUDIO_MIME_TYPE);
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(musicIntent, userId), userId,
-                STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS);
+        Intent musicIntent = new Intent(Intent.ACTION_VIEW);
+        musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
+        musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
+                AUDIO_MIME_TYPE);
+        PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackage(
+                musicIntent, userId);
+        if (musicPackage != null
+                && doesPackageSupportRuntimePermissions(musicPackage)) {
+            grantRuntimePermissions(musicPackage, STORAGE_PERMISSIONS, userId);
+            grantRuntimePermissions(musicPackage, MEDIA_AURAL_PERMISSIONS, userId);
+        }
 
         // Home
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .addCategory(Intent.CATEGORY_LAUNCHER_APP);
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(homeIntent, userId), userId,
-                LOCATION_PERMISSIONS);
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
+        PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackage(
+                homeIntent, userId);
+        if (homePackage != null
+                && doesPackageSupportRuntimePermissions(homePackage)) {
+            grantRuntimePermissions(homePackage, LOCATION_PERMISSIONS, false, userId);
+        }
 
         // Watches
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
             // Home application on watches
+            Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
+            wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
 
-            String wearPackage = getDefaultSystemHandlerActivityPackageForCategory(
-                    Intent.CATEGORY_HOME_MAIN, userId);
-            grantPermissionsToSystemPackage(wearPackage, userId,
-                    CONTACTS_PERMISSIONS, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS);
-            grantSystemFixedPermissionsToSystemPackage(wearPackage, userId, PHONE_PERMISSIONS);
+            PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackage(
+                    wearHomeIntent, userId);
+
+            if (wearHomePackage != null
+                    && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+                grantRuntimePermissions(wearHomePackage, CONTACTS_PERMISSIONS, false,
+                        userId);
+                grantRuntimePermissions(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+                grantRuntimePermissions(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+                        userId);
+                grantRuntimePermissions(wearHomePackage, LOCATION_PERMISSIONS, false,
+                        userId);
+            }
 
             // Fitness tracking on watches
-            grantPermissionsToSystemPackage(
-                    getDefaultSystemHandlerActivityPackage(ACTION_TRACK, userId), userId,
-                    SENSORS_PERMISSIONS, LOCATION_PERMISSIONS);
+            Intent trackIntent = new Intent(ACTION_TRACK);
+            PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackage(
+                    trackIntent, userId);
+            if (trackPackage != null
+                    && doesPackageSupportRuntimePermissions(trackPackage)) {
+                grantRuntimePermissions(trackPackage, SENSORS_PERMISSIONS, false, userId);
+                grantRuntimePermissions(trackPackage, LOCATION_PERMISSIONS, false, userId);
+            }
         }
 
         // Print Spooler
-        grantSystemFixedPermissionsToSystemPackage(PrintManager.PRINT_SPOOLER_PACKAGE_NAME, userId,
-                LOCATION_PERMISSIONS);
+        PackageParser.Package printSpoolerPackage = getSystemPackage(
+                PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
+        if (printSpoolerPackage != null
+                && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
+            grantRuntimePermissions(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
+        }
 
         // EmergencyInfo
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
-                        TelephonyManager.ACTION_EMERGENCY_ASSISTANCE, userId),
-                userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
+        Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
+        PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackage(
+                emergencyInfoIntent, userId);
+        if (emergencyInfoPckg != null
+                && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
+            grantRuntimePermissions(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
+            grantRuntimePermissions(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
+        }
 
         // NFC Tag viewer
-        Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW)
-                .setType("vnd.android.cursor.item/ndef_msg");
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(nfcTagIntent, userId), userId,
-                CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
+        Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
+        nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
+        PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackage(
+                nfcTagIntent, userId);
+        if (nfcTagPkg != null
+                && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
+            grantRuntimePermissions(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
+            grantRuntimePermissions(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
+        }
 
         // Storage Manager
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
-                        StorageManager.ACTION_MANAGE_STORAGE, userId),
-                userId, STORAGE_PERMISSIONS);
+        Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+        PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackage(
+                storageManagerIntent, userId);
+        if (storageManagerPckg != null
+                && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
+            grantRuntimePermissions(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
+        }
 
         // Companion devices
-        grantSystemFixedPermissionsToSystemPackage(
-                CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
-                LOCATION_PERMISSIONS);
+        PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackage(
+                CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
+        if (companionDeviceDiscoveryPackage != null
+                && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
+            grantRuntimePermissions(companionDeviceDiscoveryPackage,
+                    LOCATION_PERMISSIONS, true, userId);
+        }
 
         // Ringtone Picker
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
-                        RingtoneManager.ACTION_RINGTONE_PICKER, userId),
-                userId, STORAGE_PERMISSIONS);
+        Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+        PackageParser.Package ringtonePickerPackage =
+                getDefaultSystemHandlerActivityPackage(ringtonePickerIntent, userId);
+        if (ringtonePickerPackage != null
+                && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
+            grantRuntimePermissions(ringtonePickerPackage,
+                    STORAGE_PERMISSIONS, true, userId);
+        }
 
         // TextClassifier Service
         String textClassifierPackageName =
                 mContext.getPackageManager().getSystemTextClassifierPackageName();
         if (!TextUtils.isEmpty(textClassifierPackageName)) {
-            grantPermissionsToSystemPackage(textClassifierPackageName, userId,
-                    PHONE_PERMISSIONS, SMS_PERMISSIONS, CALENDAR_PERMISSIONS,
-                    LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
+            PackageParser.Package textClassifierPackage =
+                    getSystemPackage(textClassifierPackageName);
+            if (textClassifierPackage != null
+                    && doesPackageSupportRuntimePermissions(textClassifierPackage)) {
+                grantRuntimePermissions(textClassifierPackage, PHONE_PERMISSIONS, false, userId);
+                grantRuntimePermissions(textClassifierPackage, SMS_PERMISSIONS, false, userId);
+                grantRuntimePermissions(textClassifierPackage, CALENDAR_PERMISSIONS, false, userId);
+                grantRuntimePermissions(textClassifierPackage, LOCATION_PERMISSIONS, false, userId);
+                grantRuntimePermissions(textClassifierPackage, CONTACTS_PERMISSIONS, false, userId);
+            }
         }
 
         // There is no real "marker" interface to identify the shared storage backup, it is
         // hardcoded in BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE.
-        grantSystemFixedPermissionsToSystemPackage("com.android.sharedstoragebackup", userId,
-                STORAGE_PERMISSIONS);
+        PackageParser.Package sharedStorageBackupPackage = getSystemPackage(
+                "com.android.sharedstoragebackup");
+        if (sharedStorageBackupPackage != null) {
+            grantRuntimePermissions(sharedStorageBackupPackage, STORAGE_PERMISSIONS, true, userId);
+        }
 
         if (mPermissionGrantedCallback != null) {
             mPermissionGrantedCallback.onDefaultRuntimePermissionsGranted(userId);
         }
     }
 
-    private String getDefaultSystemHandlerActivityPackageForCategory(String category, int userId) {
-        return getDefaultSystemHandlerActivityPackage(
-                new Intent(Intent.ACTION_MAIN).addCategory(category), userId);
-    }
-
-    @SafeVarargs
-    private final void grantPermissionToEachSystemPackage(
-            ArrayList<String> packages, int userId, Set<String>... permissions) {
-        if (packages == null) return;
-        final int count = packages.size();
-        for (int i = 0; i < count; i++) {
-            grantPermissionsToSystemPackage(packages.get(i), userId, permissions);
-        }
-    }
-
-    private String getKnownPackage(int knownPkgId, int userId) {
-        return mServiceInternal.getKnownPackageName(knownPkgId, userId);
-    }
-
     private void grantDefaultPermissionsToDefaultSystemDialerApp(
-            String dialerPackage, int userId) {
-        if (dialerPackage == null) {
-            return;
+            PackageParser.Package dialerPackage, int userId) {
+        if (doesPackageSupportRuntimePermissions(dialerPackage)) {
+            boolean isPhonePermFixed =
+                    mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
+            grantRuntimePermissions(
+                    dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
+            grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, userId);
+            grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, userId);
         }
-        boolean isPhonePermFixed =
-                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
-        if (isPhonePermFixed) {
-            grantSystemFixedPermissionsToSystemPackage(dialerPackage, userId, PHONE_PERMISSIONS);
-        } else {
-            grantPermissionsToSystemPackage(dialerPackage, userId, PHONE_PERMISSIONS);
-        }
-        grantPermissionsToSystemPackage(dialerPackage, userId,
-                CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
     }
 
-    private void grantDefaultPermissionsToDefaultSystemSmsApp(String smsPackage, int userId) {
-        grantPermissionsToSystemPackage(smsPackage, userId,
-                PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
-                STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+    private void grantDefaultPermissionsToDefaultSystemSmsApp(
+            PackageParser.Package smsPackage, int userId) {
+        if (doesPackageSupportRuntimePermissions(smsPackage)) {
+            grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, userId);
+        }
     }
 
     private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
-            String useOpenWifiPackage, int userId) {
-        grantPermissionsToSystemPackage(
-                useOpenWifiPackage, userId, COARSE_LOCATION_PERMISSIONS);
+            PackageParser.Package useOpenWifiPackage, int userId) {
+        if (doesPackageSupportRuntimePermissions(useOpenWifiPackage)) {
+            grantRuntimePermissions(useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, userId);
+        }
     }
 
     public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
-        grantIgnoringSystemPackage(packageName, userId,
-                PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS, STORAGE_PERMISSIONS,
-                MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package smsPackage = getPackage(packageName);
+        if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
+            grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
+        }
     }
 
     public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
-        mServiceInternal.onDefaultDialerAppChanged(packageName, userId);
         Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
-        grantIgnoringSystemPackage(packageName, userId,
-                PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
-                MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package dialerPackage = getPackage(packageName);
+        if (dialerPackage != null
+                && doesPackageSupportRuntimePermissions(dialerPackage)) {
+            grantRuntimePermissions(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
+        }
     }
 
     public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default Use Open WiFi app for user:" + userId);
-        grantIgnoringSystemPackage(packageName, userId, COARSE_LOCATION_PERMISSIONS);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package useOpenWifiPackage = getPackage(packageName);
+        if (useOpenWifiPackage != null
+                && doesPackageSupportRuntimePermissions(useOpenWifiPackage)) {
+            grantRuntimePermissions(
+                    useOpenWifiPackage, COARSE_LOCATION_PERMISSIONS, false, true, userId);
+        }
+    }
+
+    private void grantDefaultPermissionsToDefaultSimCallManager(
+            PackageParser.Package simCallManagerPackage, int userId) {
+        Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
+        if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
+            grantRuntimePermissions(simCallManagerPackage, PHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
+        }
     }
 
     public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
         if (packageName == null) {
             return;
         }
-        Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
-        grantRuntimePermissionsToPackage(packageName, userId, false, false,
-                PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS);
-    }
-
-    private void grantDefaultPermissionsToDefaultSystemSimCallManager(
-            String packageName, int userId) {
-        if (isSystemPackage(packageName)) {
-            grantDefaultPermissionsToDefaultSimCallManager(packageName, userId);
+        PackageParser.Package simCallManagerPackage = getPackage(packageName);
+        if (simCallManagerPackage != null) {
+            grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, userId);
         }
     }
 
@@ -801,8 +976,13 @@
             return;
         }
         for (String packageName : packageNames) {
-            grantPermissionsToSystemPackage(packageName, userId,
-                    PHONE_PERMISSIONS, LOCATION_PERMISSIONS, SMS_PERMISSIONS);
+            PackageParser.Package carrierPackage = getSystemPackage(packageName);
+            if (carrierPackage != null
+                    && doesPackageSupportRuntimePermissions(carrierPackage)) {
+                grantRuntimePermissions(carrierPackage, PHONE_PERMISSIONS, userId);
+                grantRuntimePermissions(carrierPackage, LOCATION_PERMISSIONS, userId);
+                grantRuntimePermissions(carrierPackage, SMS_PERMISSIONS, userId);
+            }
         }
     }
 
@@ -812,9 +992,15 @@
             return;
         }
         for (String packageName : packageNames) {
-            grantPermissionsToSystemPackage(packageName, userId,
-                    PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS, LOCATION_PERMISSIONS,
-                    CAMERA_PERMISSIONS, CONTACTS_PERMISSIONS);
+            PackageParser.Package imsServicePackage = getSystemPackage(packageName);
+            if (imsServicePackage != null
+                    && doesPackageSupportRuntimePermissions(imsServicePackage)) {
+                grantRuntimePermissions(imsServicePackage, PHONE_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, CONTACTS_PERMISSIONS, userId);
+            }
         }
     }
 
@@ -825,12 +1011,15 @@
             return;
         }
         for (String packageName : packageNames) {
-            // Grant these permissions as system-fixed, so that nobody can accidentally
-            // break cellular data.
-            grantSystemFixedPermissionsToSystemPackage(packageName, userId,
-                    PHONE_PERMISSIONS, LOCATION_PERMISSIONS);
+            PackageParser.Package dataServicePackage = getSystemPackage(packageName);
+            if (dataServicePackage != null
+                    && doesPackageSupportRuntimePermissions(dataServicePackage)) {
+                // Grant these permissions as system-fixed, so that nobody can accidentally
+                // break cellular data.
+                grantRuntimePermissions(dataServicePackage, PHONE_PERMISSIONS, true, userId);
+                grantRuntimePermissions(dataServicePackage, LOCATION_PERMISSIONS, true, userId);
+            }
         }
-
     }
 
     public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
@@ -840,17 +1029,25 @@
             return;
         }
         for (String packageName : packageNames) {
-            PackageInfo pkg = getSystemPackageInfo(packageName);
-            if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
-                revokeRuntimePermissions(packageName, PHONE_PERMISSIONS, true, userId);
-                revokeRuntimePermissions(packageName, LOCATION_PERMISSIONS, true, userId);
+            PackageParser.Package dataServicePackage = getSystemPackage(packageName);
+            if (dataServicePackage != null
+                    && doesPackageSupportRuntimePermissions(dataServicePackage)) {
+                revokeRuntimePermissions(dataServicePackage, PHONE_PERMISSIONS, true, userId);
+                revokeRuntimePermissions(dataServicePackage, LOCATION_PERMISSIONS, true, userId);
             }
         }
     }
 
     public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to active LUI app for user:" + userId);
-        grantSystemFixedPermissionsToSystemPackage(packageName, userId, CAMERA_PERMISSIONS);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package luiAppPackage = getSystemPackage(packageName);
+        if (luiAppPackage != null
+                && doesPackageSupportRuntimePermissions(luiAppPackage)) {
+            grantRuntimePermissions(luiAppPackage, CAMERA_PERMISSIONS, true, userId);
+        }
     }
 
     public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
@@ -859,116 +1056,123 @@
             return;
         }
         for (String packageName : packageNames) {
-            PackageInfo pkg = getSystemPackageInfo(packageName);
-            if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
-                revokeRuntimePermissions(packageName, CAMERA_PERMISSIONS, true, userId);
+            PackageParser.Package luiAppPackage = getSystemPackage(packageName);
+            if (luiAppPackage != null
+                    && doesPackageSupportRuntimePermissions(luiAppPackage)) {
+                revokeRuntimePermissions(luiAppPackage, CAMERA_PERMISSIONS, true, userId);
             }
         }
     }
 
     public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default browser for user:" + userId);
-        grantPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package browserPackage = getSystemPackage(packageName);
+        if (browserPackage != null
+                && doesPackageSupportRuntimePermissions(browserPackage)) {
+            grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
+        }
     }
 
-    private String getDefaultSystemHandlerActivityPackage(String intentAction, int userId) {
-        return getDefaultSystemHandlerActivityPackage(new Intent(intentAction), userId);
-    }
-
-    private String getDefaultSystemHandlerActivityPackage(Intent intent, int userId) {
+    private PackageParser.Package getDefaultSystemHandlerActivityPackage(
+            Intent intent, int userId) {
         ResolveInfo handler = mServiceInternal.resolveIntent(intent,
-                intent.resolveType(mContext.getContentResolver()), DEFAULT_INTENT_QUERY_FLAGS,
-                userId, false, Binder.getCallingUid());
+                intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false,
+                Binder.getCallingUid());
         if (handler == null || handler.activityInfo == null) {
             return null;
         }
         if (mServiceInternal.isResolveActivityComponent(handler.activityInfo)) {
             return null;
         }
-        String packageName = handler.activityInfo.packageName;
-        return isSystemPackage(packageName) ? packageName : null;
+        return getSystemPackage(handler.activityInfo.packageName);
     }
 
-    private String getDefaultSystemHandlerServicePackage(String intentAction, int userId) {
-        return getDefaultSystemHandlerServicePackage(new Intent(intentAction), userId);
-    }
-
-    private String getDefaultSystemHandlerServicePackage(
+    private PackageParser.Package getDefaultSystemHandlerServicePackage(
             Intent intent, int userId) {
         List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
-                intent, DEFAULT_INTENT_QUERY_FLAGS, Binder.getCallingUid(), userId);
+                intent, DEFAULT_FLAGS, Binder.getCallingUid(), userId);
         if (handlers == null) {
             return null;
         }
         final int handlerCount = handlers.size();
         for (int i = 0; i < handlerCount; i++) {
             ResolveInfo handler = handlers.get(i);
-            String handlerPackage = handler.serviceInfo.packageName;
-            if (isSystemPackage(handlerPackage)) {
+            PackageParser.Package handlerPackage = getSystemPackage(
+                    handler.serviceInfo.packageName);
+            if (handlerPackage != null) {
                 return handlerPackage;
             }
         }
         return null;
     }
 
-    private ArrayList<String> getHeadlessSyncAdapterPackages(
+    private List<PackageParser.Package> getHeadlessSyncAdapterPackages(
             String[] syncAdapterPackageNames, int userId) {
-        ArrayList<String> syncAdapterPackages = new ArrayList<>();
+        List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
 
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
 
         for (String syncAdapterPackageName : syncAdapterPackageNames) {
             homeIntent.setPackage(syncAdapterPackageName);
 
             ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
-                    homeIntent.resolveType(mContext.getContentResolver()),
-                    DEFAULT_INTENT_QUERY_FLAGS,
+                    homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS,
                     userId, false, Binder.getCallingUid());
             if (homeActivity != null) {
                 continue;
             }
 
-            if (isSystemPackage(syncAdapterPackageName)) {
-                syncAdapterPackages.add(syncAdapterPackageName);
+            PackageParser.Package syncAdapterPackage = getSystemPackage(syncAdapterPackageName);
+            if (syncAdapterPackage != null) {
+                syncAdapterPackages.add(syncAdapterPackage);
             }
         }
 
         return syncAdapterPackages;
     }
 
-    private String getDefaultProviderAuthorityPackage(String authority, int userId) {
-        ProviderInfo provider = mServiceInternal.resolveContentProvider(
-                authority, DEFAULT_INTENT_QUERY_FLAGS, userId);
+    private PackageParser.Package getDefaultProviderAuthorityPackage(
+            String authority, int userId) {
+        ProviderInfo provider =
+                mServiceInternal.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
         if (provider != null) {
-            return provider.packageName;
+            return getSystemPackage(provider.packageName);
         }
         return null;
     }
 
-    private boolean isSystemPackage(String packageName) {
-        return isSystemPackage(getSystemPackageInfo(packageName));
+    private PackageParser.Package getPackage(String packageName) {
+        return mServiceInternal.getPackage(packageName);
     }
 
-    private boolean isSystemPackage(PackageInfo pkg) {
-        if (pkg == null) {
-            return false;
+    private PackageParser.Package getSystemPackage(String packageName) {
+        PackageParser.Package pkg = getPackage(packageName);
+        if (pkg != null && pkg.isSystem()) {
+            return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
         }
-        return pkg.applicationInfo.isSystemApp()
-                && !isSysComponentOrPersistentPlatformSignedPrivApp(pkg);
+        return null;
     }
 
-    private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissions,
+    private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+            int userId) {
+        grantRuntimePermissions(pkg, permissions, false, false, userId);
+    }
+
+    private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
             boolean systemFixed, int userId) {
         grantRuntimePermissions(pkg, permissions, systemFixed, false, userId);
     }
 
-    private void revokeRuntimePermissions(String packageName, Set<String> permissions,
+    private void revokeRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
             boolean systemFixed, int userId) {
-        PackageInfo pkg = getSystemPackageInfo(packageName);
-        if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+        if (pkg.requestedPermissions.isEmpty()) {
             return;
         }
-        Set<String> revokablePermissions = new ArraySet<>(Arrays.asList(pkg.requestedPermissions));
+        Set<String> revokablePermissions = new ArraySet<>(pkg.requestedPermissions);
 
         for (String permission : permissions) {
             // We can't revoke what wasn't requested.
@@ -977,7 +1181,7 @@
             }
 
             final int flags = mServiceInternal.getPermissionFlagsTEMP(
-                    permission, packageName, userId);
+                    permission, pkg.packageName, userId);
 
             // We didn't get this through the default grant policy. Move along.
             if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) == 0) {
@@ -993,35 +1197,29 @@
             if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && !systemFixed) {
                 continue;
             }
-            mServiceInternal.revokeRuntimePermission(packageName, permission, userId, false);
+            mServiceInternal.revokeRuntimePermission(pkg.packageName, permission, userId, false);
 
             if (DEBUG) {
                 Log.i(TAG, "revoked " + (systemFixed ? "fixed " : "not fixed ")
-                        + permission + " to " + packageName);
+                        + permission + " to " + pkg.packageName);
             }
 
             // Remove the GRANTED_BY_DEFAULT flag without touching the others.
             // Note that we do not revoke FLAG_PERMISSION_SYSTEM_FIXED. That bit remains
             // sticky once set.
-            mServiceInternal.updatePermissionFlagsTEMP(permission, packageName,
+            mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
                     PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, userId);
         }
     }
 
-    private void grantRuntimePermissions(PackageInfo pkg,
+    private void grantRuntimePermissions(PackageParser.Package pkg,
             Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage,
             int userId) {
-        if (pkg == null) {
-            return;
-        }
-
-        String[] requestedPermissions = pkg.requestedPermissions;
-        if (ArrayUtils.isEmpty(requestedPermissions)) {
+        if (pkg.requestedPermissions.isEmpty()) {
             return;
         }
 
         final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
-        ApplicationInfo applicationInfo = pkg.applicationInfo;
 
         // Automatically attempt to grant split permissions to older APKs
         final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
@@ -1029,13 +1227,13 @@
             final PackageParser.SplitPermissionInfo splitPerm =
                     PackageParser.SPLIT_PERMISSIONS[splitPermNum];
 
-            if (applicationInfo != null
-                    && applicationInfo.targetSdkVersion < splitPerm.targetSdk
+            if (pkg.applicationInfo.targetSdkVersion < splitPerm.targetSdk
                     && permissionsWithoutSplits.contains(splitPerm.rootPerm)) {
                 Collections.addAll(permissions, splitPerm.newPerms);
             }
         }
 
+        List<String> requestedPermissions = pkg.requestedPermissions;
         Set<String> grantablePermissions = null;
 
         // In some cases, like for the Phone or SMS app, we grant permissions regardless
@@ -1044,25 +1242,23 @@
         // choice to grant this app the permissions needed to function. For all other
         // apps, (default grants on first boot and user creation) we don't grant default
         // permissions if the version on the system image does not declare them.
-        if (!ignoreSystemPackage
-                && applicationInfo != null
-                && applicationInfo.isUpdatedSystemApp()) {
-            final PackageInfo disabledPkg = getSystemPackageInfo(
-                    mServiceInternal.getDisabledSystemPackageName(pkg.packageName));
+        if (!ignoreSystemPackage && pkg.isUpdatedSystemApp()) {
+            final PackageParser.Package disabledPkg =
+                    mServiceInternal.getDisabledPackage(pkg.packageName);
             if (disabledPkg != null) {
-                if (ArrayUtils.isEmpty(disabledPkg.requestedPermissions)) {
+                if (disabledPkg.requestedPermissions.isEmpty()) {
                     return;
                 }
                 if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) {
-                    grantablePermissions = new ArraySet<>(Arrays.asList(requestedPermissions));
+                    grantablePermissions = new ArraySet<>(requestedPermissions);
                     requestedPermissions = disabledPkg.requestedPermissions;
                 }
             }
         }
 
-        final int grantablePermissionCount = requestedPermissions.length;
+        final int grantablePermissionCount = requestedPermissions.size();
         for (int i = 0; i < grantablePermissionCount; i++) {
-            String permission = requestedPermissions[i];
+            String permission = requestedPermissions.get(i);
 
             // If there is a disabled system app it may request a permission the updated
             // version ot the data partition doesn't, In this case skip the permission.
@@ -1092,7 +1288,7 @@
                             pkg.packageName, permission, userId, false);
                     if (DEBUG) {
                         Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
-                                + permission + " to default handler " + pkg);
+                                + permission + " to default handler " + pkg.packageName);
                     }
 
                     int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -1111,7 +1307,7 @@
                         && !systemFixed) {
                     if (DEBUG) {
                         Log.i(TAG, "Granted not fixed " + permission + " to default handler "
-                                + pkg);
+                                + pkg.packageName);
                     }
                     mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
                             PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
@@ -1120,42 +1316,28 @@
         }
     }
 
-    private PackageInfo getSystemPackageInfo(String pkg) {
-        //TODO not MATCH_SYSTEM_ONLY?
-        return getPackageInfo(pkg, PackageManager.MATCH_FACTORY_ONLY);
-    }
-
-    private PackageInfo getPackageInfo(String pkg) {
-        return getPackageInfo(pkg, 0 /* extraFlags */);
-    }
-
-    private PackageInfo getPackageInfo(String pkg,
-            @PackageManager.PackageInfoFlags int extraFlags) {
-        return mServiceInternal.getPackageInfo(pkg,
-                DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags,
-                //TODO is this the right filterCallingUid?
-                UserHandle.USER_SYSTEM, UserHandle.USER_SYSTEM);
-    }
-
-    private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageInfo pkg) {
+    private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) {
         if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
             return true;
         }
-        if (!pkg.applicationInfo.isPrivilegedApp()) {
+        if (!pkg.isPrivileged()) {
             return false;
         }
-        final PackageInfo disabledPkg = getSystemPackageInfo(
-                mServiceInternal.getDisabledSystemPackageName(pkg.applicationInfo.packageName));
+        final PackageParser.Package disabledPkg =
+                mServiceInternal.getDisabledPackage(pkg.packageName);
         if (disabledPkg != null) {
-            ApplicationInfo disabledPackageAppInfo = disabledPkg.applicationInfo;
-            if (disabledPackageAppInfo != null
-                    && (disabledPackageAppInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+            if ((disabledPkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
                 return false;
             }
         } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
             return false;
         }
-        return mServiceInternal.isPlatformSigned(pkg.packageName);
+        final String systemPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+        final PackageParser.Package systemPackage = getPackage(systemPackageName);
+        return pkg.mSigningDetails.hasAncestorOrSelf(systemPackage.mSigningDetails)
+                || systemPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
+                        PackageParser.SigningDetails.CertCapabilities.PERMISSION);
     }
 
     private void grantDefaultPermissionExceptions(int userId) {
@@ -1175,7 +1357,7 @@
         final int exceptionCount = mGrantExceptions.size();
         for (int i = 0; i < exceptionCount; i++) {
             String packageName = mGrantExceptions.keyAt(i);
-            PackageInfo pkg = getSystemPackageInfo(packageName);
+            PackageParser.Package pkg = getSystemPackage(packageName);
             List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
             final int permissionGrantCount = permissionGrants.size();
             for (int j = 0; j < permissionGrantCount; j++) {
@@ -1186,7 +1368,8 @@
                     permissions.clear();
                 }
                 permissions.add(permissionGrant.name);
-                grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, userId);
+                grantRuntimePermissions(pkg, permissions,
+                        permissionGrant.fixed, userId);
             }
         }
     }
@@ -1291,14 +1474,15 @@
                         outGrantExceptions.get(packageName);
                 if (packageExceptions == null) {
                     // The package must be on the system image
-                    if (!isSystemPackage(packageName)) {
+                    PackageParser.Package pkg = getSystemPackage(packageName);
+                    if (pkg == null) {
                         Log.w(TAG, "Unknown package:" + packageName);
                         XmlUtils.skipCurrentTag(parser);
                         continue;
                     }
 
                     // The package must support runtime permissions
-                    if (!doesPackageSupportRuntimePermissions(getSystemPackageInfo(packageName))) {
+                    if (!doesPackageSupportRuntimePermissions(pkg)) {
                         Log.w(TAG, "Skipping non supporting runtime permissions package:"
                                 + packageName);
                         XmlUtils.skipCurrentTag(parser);
@@ -1343,9 +1527,8 @@
         }
     }
 
-    private static boolean doesPackageSupportRuntimePermissions(PackageInfo pkg) {
-        return pkg.applicationInfo != null
-                && pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+    private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
+        return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
     }
 
     private static final class DefaultPermissionGrant {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4b6760c..c4f90a12 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1189,7 +1189,7 @@
                 // is granted only if it had been defined by the original application.
                 if (pkg.isUpdatedSystemApp()) {
                     final PackageParser.Package disabledPkg =
-                            mPackageManagerInt.getDisabledSystemPackage(pkg.packageName);
+                            mPackageManagerInt.getDisabledPackage(pkg.packageName);
                     final PackageSetting disabledPs =
                             (disabledPkg != null) ? (PackageSetting) disabledPkg.mExtras : null;
                     if (disabledPs != null
@@ -1221,7 +1221,7 @@
                         // packages can also get the permission.
                         if (pkg.parentPackage != null) {
                             final PackageParser.Package disabledParentPkg = mPackageManagerInt
-                                    .getDisabledSystemPackage(pkg.parentPackage.packageName);
+                                    .getDisabledPackage(pkg.parentPackage.packageName);
                             final PackageSetting disabledParentPs = (disabledParentPkg != null)
                                     ? (PackageSetting) disabledParentPkg.mExtras : null;
                             if (disabledParentPkg != null
@@ -1372,7 +1372,7 @@
             return;
         }
         final PackageParser.Package disabledPkg =
-                mPackageManagerInt.getDisabledSystemPackage(pkg.parentPackage.packageName);
+                mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName);
         if (disabledPkg == null || disabledPkg.mExtras == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 7a3f030..6c5452a 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
@@ -38,13 +39,12 @@
 import android.util.IntArray;
 import android.util.Slog;
 
+import android.util.SparseBooleanArray;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.telephony.SmsApplication;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerService;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.PermissionManagerInternal;
 
 /**
  * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
@@ -72,8 +72,8 @@
                 synchronized (mLock) {
                     if (mDefaultSmsAppRequests != null || mDefaultDialerAppRequests != null
                             || mDefaultSimCallManagerRequests != null) {
-                        final DefaultPermissionGrantPolicy permissionPolicy =
-                                getDefaultPermissionGrantPolicy();
+                        final PackageManagerInternal packageManagerInternal = LocalServices
+                                .getService(PackageManagerInternal.class);
 
                         if (mDefaultSmsAppRequests != null) {
                             ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
@@ -83,7 +83,7 @@
                                 for (int i = requestCount - 1; i >= 0; i--) {
                                     final int userid = mDefaultSmsAppRequests.get(i);
                                     mDefaultSmsAppRequests.remove(i);
-                                    permissionPolicy.grantDefaultPermissionsToDefaultSmsApp(
+                                    packageManagerInternal.grantDefaultPermissionsToDefaultSmsApp(
                                             smsComponent.getPackageName(), userid);
                                 }
                             }
@@ -97,7 +97,7 @@
                                 for (int i = requestCount - 1; i >= 0; i--) {
                                     final int userId = mDefaultDialerAppRequests.get(i);
                                     mDefaultDialerAppRequests.remove(i);
-                                    permissionPolicy.grantDefaultPermissionsToDefaultDialerApp(
+                                    packageManagerInternal.grantDefaultPermissionsToDefaultDialerApp(
                                             packageName, userId);
                                 }
                             }
@@ -113,7 +113,7 @@
                                 for (int i = requestCount - 1; i >= 0; i--) {
                                     final int userId = mDefaultSimCallManagerRequests.get(i);
                                     mDefaultSimCallManagerRequests.remove(i);
-                                    permissionPolicy
+                                    packageManagerInternal
                                             .grantDefaultPermissionsToDefaultSimCallManager(
                                                     packageName, userId);
                                 }
@@ -132,11 +132,6 @@
         }
     }
 
-    private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
-        return LocalServices.getService(PermissionManagerInternal.class)
-                .getDefaultPermissionGrantPolicy();
-    }
-
     private static final ComponentName SERVICE_COMPONENT = new ComponentName(
             "com.android.server.telecom",
             "com.android.server.telecom.components.TelecomService");
@@ -201,68 +196,82 @@
 
 
     private void registerDefaultAppProviders() {
-        final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy();
+        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
 
-        // Set a callback for the permission grant policy to query the default sms app.
-        permissionPolicy.setSmsAppPackagesProvider(userId -> {
-            synchronized (mLock) {
-                if (mServiceConnection == null) {
-                    if (mDefaultSmsAppRequests == null) {
-                        mDefaultSmsAppRequests = new IntArray();
+        // Set a callback for the package manager to query the default sms app.
+        packageManagerInternal.setSmsAppPackagesProvider(
+                new PackageManagerInternal.PackagesProvider() {
+            @Override
+            public String[] getPackages(int userId) {
+                synchronized (mLock) {
+                    if (mServiceConnection == null) {
+                        if (mDefaultSmsAppRequests == null) {
+                            mDefaultSmsAppRequests = new IntArray();
+                        }
+                        mDefaultSmsAppRequests.add(userId);
+                        return null;
                     }
-                    mDefaultSmsAppRequests.add(userId);
-                    return null;
                 }
+                ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
+                        mContext, true);
+                if (smsComponent != null) {
+                    return new String[]{smsComponent.getPackageName()};
+                }
+                return null;
             }
-            ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
-                    mContext, true);
-            if (smsComponent != null) {
-                return new String[]{smsComponent.getPackageName()};
-            }
-            return null;
         });
 
-        // Set a callback for the permission grant policy to query the default dialer app.
-        permissionPolicy.setDialerAppPackagesProvider(userId -> {
-            synchronized (mLock) {
-                if (mServiceConnection == null) {
-                    if (mDefaultDialerAppRequests == null) {
-                        mDefaultDialerAppRequests = new IntArray();
+        // Set a callback for the package manager to query the default dialer app.
+        packageManagerInternal.setDialerAppPackagesProvider(
+                new PackageManagerInternal.PackagesProvider() {
+            @Override
+            public String[] getPackages(int userId) {
+                synchronized (mLock) {
+                    if (mServiceConnection == null) {
+                        if (mDefaultDialerAppRequests == null) {
+                            mDefaultDialerAppRequests = new IntArray();
+                        }
+                        mDefaultDialerAppRequests.add(userId);
+                        return null;
                     }
-                    mDefaultDialerAppRequests.add(userId);
-                    return null;
                 }
+                String packageName = DefaultDialerManager.getDefaultDialerApplication(mContext);
+                if (packageName != null) {
+                    return new String[]{packageName};
+                }
+                return null;
             }
-            String packageName = DefaultDialerManager.getDefaultDialerApplication(mContext);
-            if (packageName != null) {
-                return new String[]{packageName};
-            }
-            return null;
         });
 
-        // Set a callback for the permission grant policy to query the default sim call manager.
-        permissionPolicy.setSimCallManagerPackagesProvider(userId -> {
-            synchronized (mLock) {
-                if (mServiceConnection == null) {
-                    if (mDefaultSimCallManagerRequests == null) {
-                        mDefaultSimCallManagerRequests = new IntArray();
+        // Set a callback for the package manager to query the default sim call manager.
+        packageManagerInternal.setSimCallManagerPackagesProvider(
+                new PackageManagerInternal.PackagesProvider() {
+            @Override
+            public String[] getPackages(int userId) {
+                synchronized (mLock) {
+                    if (mServiceConnection == null) {
+                        if (mDefaultSimCallManagerRequests == null) {
+                            mDefaultSimCallManagerRequests = new IntArray();
+                        }
+                        mDefaultSimCallManagerRequests.add(userId);
+                        return null;
                     }
-                    mDefaultSimCallManagerRequests.add(userId);
-                    return null;
                 }
-            }
-            TelecomManager telecomManager =
+                TelecomManager telecomManager =
                     (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-            PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
-            if (phoneAccount != null) {
-                return new String[]{phoneAccount.getComponentName().getPackageName()};
+                PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
+                if (phoneAccount != null) {
+                    return new String[]{phoneAccount.getComponentName().getPackageName()};
+                }
+                return null;
             }
-            return null;
         });
     }
 
     private void registerDefaultAppNotifier() {
-        final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy();
+        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
 
         // Notify the package manager on default app changes
         final Uri defaultSmsAppUri = Settings.Secure.getUriFor(
@@ -278,17 +287,17 @@
                     ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
                             mContext, true);
                     if (smsComponent != null) {
-                        permissionPolicy.grantDefaultPermissionsToDefaultSmsApp(
+                        packageManagerInternal.grantDefaultPermissionsToDefaultSmsApp(
                                 smsComponent.getPackageName(), userId);
                     }
                 } else if (defaultDialerAppUri.equals(uri)) {
                     String packageName = DefaultDialerManager.getDefaultDialerApplication(
                             mContext);
                     if (packageName != null) {
-                        permissionPolicy.grantDefaultPermissionsToDefaultDialerApp(
+                        packageManagerInternal.grantDefaultPermissionsToDefaultDialerApp(
                                 packageName, userId);
                     }
-                    updateSimCallManagerPermissions(permissionPolicy, userId);
+                    updateSimCallManagerPermissions(packageManagerInternal, userId);
                 }
             }
         };
@@ -301,12 +310,14 @@
 
 
     private void registerCarrierConfigChangedReceiver() {
+        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
         BroadcastReceiver receiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
                     for (int userId : UserManagerService.getInstance().getUserIds()) {
-                        updateSimCallManagerPermissions(getDefaultPermissionGrantPolicy(), userId);
+                        updateSimCallManagerPermissions(packageManagerInternal, userId);
                     }
                 }
             }
@@ -316,15 +327,15 @@
             new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null, null);
     }
 
-    private void updateSimCallManagerPermissions(
-            DefaultPermissionGrantPolicy permissionGrantPolicy, int userId) {
+    private void updateSimCallManagerPermissions(PackageManagerInternal packageManagerInternal,
+            int userId) {
         TelecomManager telecomManager =
             (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
         PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
         if (phoneAccount != null) {
             Slog.i(TAG, "updating sim call manager permissions for userId:" + userId);
             String packageName = phoneAccount.getComponentName().getPackageName();
-            permissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+            packageManagerInternal.grantDefaultPermissionsToDefaultSimCallManager(
                 packageName, userId);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2ed09ae..ee128c7 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 inputMethodClientHasFocus(IInputMethodClient client) {
+            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)) {
+                        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;
                 }
             }
-
-            // 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/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/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 61c38fb..7506d00 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1087,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
@@ -2273,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);
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/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..6d78990 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -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");
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/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/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/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