Merge "Make SubscriptionManager.getSlotIndex public"
diff --git a/Android.bp b/Android.bp
index 6266abe..415eff3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,6 +368,7 @@
         "core/java/com/android/internal/app/IAppOpsService.aidl",
         "core/java/com/android/internal/app/IBatteryStats.aidl",
         "core/java/com/android/internal/app/ISoundTriggerService.aidl",
+        "core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl",
         "core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
         "core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
         "core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
@@ -1590,6 +1591,7 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -1614,6 +1616,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 d96efbd..3beb491 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -14015,12 +14015,15 @@
     method public android.graphics.Paint.Style getStyle();
     method public android.graphics.Paint.Align getTextAlign();
     method public void getTextBounds(java.lang.String, int, int, android.graphics.Rect);
+    method public void getTextBounds(java.lang.CharSequence, int, int, android.graphics.Rect);
     method public void getTextBounds(char[], int, int, android.graphics.Rect);
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
     method public void getTextPath(char[], int, int, float, float, android.graphics.Path);
     method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path);
     method public float getTextRunAdvances(char[], int, int, int, int, boolean, float[], int);
+    method public int getTextRunCursor(char[], int, int, boolean, int, int);
+    method public int getTextRunCursor(java.lang.CharSequence, int, int, boolean, int, int);
     method public float getTextScaleX();
     method public float getTextSize();
     method public float getTextSkewX();
@@ -14087,6 +14090,11 @@
     method public void setWordSpacing(float);
     method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
     field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
+    field public static final int CURSOR_AFTER = 0; // 0x0
+    field public static final int CURSOR_AT = 4; // 0x4
+    field public static final int CURSOR_AT_OR_AFTER = 1; // 0x1
+    field public static final int CURSOR_AT_OR_BEFORE = 3; // 0x3
+    field public static final int CURSOR_BEFORE = 2; // 0x2
     field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
     field public static final int DITHER_FLAG = 4; // 0x4
     field public static final int EMBEDDED_BITMAP_TEXT_FLAG = 1024; // 0x400
@@ -39920,6 +39928,7 @@
     method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
+    method public java.util.Set<java.lang.String> onGetSupportedVoiceActions(java.util.Set<java.lang.String>);
     method public void onLaunchVoiceAssistFromKeyguard();
     method public void onReady();
     method public void onShutdown();
diff --git a/api/system-current.txt b/api/system-current.txt
index 47b4cfc..8f7606a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5405,7 +5405,6 @@
   }
 
   public class TelephonyManager {
-    method public deprecated void answerRingingCall();
     method public deprecated void call(java.lang.String, java.lang.String);
     method public int checkCarrierPrivilegesForPackage(java.lang.String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
@@ -5413,7 +5412,6 @@
     method public boolean disableDataConnectivity();
     method public boolean enableDataConnectivity();
     method public void enableVideoCalling(boolean);
-    method public deprecated boolean endCall();
     method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index b88c760..9012c33 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -143,3 +143,12 @@
 
 }
 
+package android.telephony {
+
+  public class TelephonyManager {
+    method public deprecated void answerRingingCall();
+    method public deprecated boolean endCall();
+  }
+
+}
+
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 36e51b9..12fb4a3 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -470,7 +470,8 @@
                     onExecute(provider);
                 } finally {
                     if (provider != null) {
-                        activityManager.removeContentProviderExternal(providerName, token);
+                        activityManager.removeContentProviderExternalAsUser(
+                                providerName, token, mUserId);
                     }
                 }
             } catch (Exception e) {
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index e14f2eb..d334f96 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -198,8 +198,7 @@
 
     sp<GraphicBuffer> outBuffer;
     status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
-            0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
-            &outBuffer);
+            0 /* reqHeight */, false, captureOrientation, &outBuffer);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 7b6d29b..fc1a61c 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -147,6 +147,9 @@
         case STRING:
             str_value = from.str_value;
             break;
+        case STORAGE:
+            storage_value = from.storage_value;
+            break;
         default:
             break;
     }
@@ -164,6 +167,8 @@
             return std::to_string(double_value) + "[D]";
         case STRING:
             return str_value + "[S]";
+        case STORAGE:
+            return "bytes of size " + std::to_string(storage_value.size()) + "[ST]";
         default:
             return "[UNKNOWN]";
     }
@@ -183,6 +188,8 @@
             return double_value == that.double_value;
         case STRING:
             return str_value == that.str_value;
+        case STORAGE:
+            return storage_value == that.storage_value;
         default:
             return false;
     }
@@ -201,6 +208,8 @@
             return double_value != that.double_value;
         case STRING:
             return str_value != that.str_value;
+        case STORAGE:
+            return storage_value != that.storage_value;
         default:
             return false;
     }
@@ -220,6 +229,8 @@
             return double_value < that.double_value;
         case STRING:
             return str_value < that.str_value;
+        case STORAGE:
+            return storage_value < that.storage_value;
         default:
             return false;
     }
@@ -239,6 +250,8 @@
             return double_value > that.double_value;
         case STRING:
             return str_value > that.str_value;
+        case STORAGE:
+            return storage_value > that.storage_value;
         default:
             return false;
     }
@@ -258,6 +271,8 @@
             return double_value >= that.double_value;
         case STRING:
             return str_value >= that.str_value;
+        case STORAGE:
+            return storage_value >= that.storage_value;
         default:
             return false;
     }
@@ -274,6 +289,11 @@
         return v;
     }
 
+    if (type == STORAGE) {
+        ALOGE("Can't operate on storage value type");
+        return v;
+    }
+
     switch (type) {
         case INT:
             v.setInt(int_value - that.int_value);
@@ -311,6 +331,9 @@
         case STRING:
             str_value = that.str_value;
             break;
+        case STORAGE:
+            storage_value = that.storage_value;
+            break;
         default:
             break;
     }
@@ -326,6 +349,10 @@
         ALOGE("Can't operate on string value type");
         return *this;
     }
+    if (type == STORAGE) {
+        ALOGE("Can't operate on storage value type");
+        return *this;
+    }
 
     switch (type) {
         case INT:
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index b1b885e..77163f9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -32,7 +32,7 @@
 const int32_t kClearLastBitDeco = 0x7f;
 const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
 
-enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE };
 
 int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
 
@@ -293,6 +293,11 @@
         type = STRING;
     }
 
+    Value(const std::vector<uint8_t>& v) {
+        storage_value = v;
+        type = STORAGE;
+    }
+
     void setInt(int32_t v) {
         int_value = v;
         type = INT;
@@ -320,6 +325,7 @@
         double double_value;
     };
     std::string str_value;
+    std::vector<uint8_t> storage_value;
 
     Type type;
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 13ae41d..c6c10ec 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -170,9 +170,9 @@
         DirectoryUsage directory_usage = 10026;
         AppSize app_size = 10027;
         CategorySize category_size = 10028;
-        android.service.procstats.ProcessStatsSectionProto proc_stats = 10029;
         BatteryVoltage battery_voltage = 10030;
         NumFingerprints num_fingerprints = 10031;
+        ProcStats proc_stats = 10029;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -2339,7 +2339,7 @@
  * Next tag: 11
  */
 message LooperStats {
-    // Currently not collected and always set to 0.
+    // The uid that made a call to the System Server and caused the message to be enqueued.
     optional int32 uid = 1 [(is_uid) = true];
 
     // Fully qualified class name of the handler target class.
@@ -2479,3 +2479,10 @@
     // Number of fingerprints registered to that user.
     optional int32 num_fingerprints = 2;
 }
+
+/**
+ * Pulled from ProcessStatsService.java
+ */
+message ProcStats {
+    optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
+}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index d953f50..6d7bba0 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -36,8 +36,6 @@
 namespace os {
 namespace statsd {
 
-const int kLogMsgHeaderSize = 28;
-
 // The reading and parsing are implemented in Java. It is not difficult to port over. But for now
 // let StatsCompanionService handle that and send the data back.
 StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) {
@@ -56,20 +54,12 @@
         vector<StatsLogEventWrapper> returned_value;
         Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
         if (!status.isOk()) {
-            ALOGW("error pulling for %d", mTagId);
+            ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
             return false;
         }
         data->clear();
-        int32_t timestampSec = getWallClockSec();
         for (const StatsLogEventWrapper& it : returned_value) {
-            log_msg tmp;
-            tmp.entry_v1.len = it.bytes.size();
-            // Manually set the header size to 28 bytes to match the pushed log events.
-            tmp.entry.hdr_size = kLogMsgHeaderSize;
-            tmp.entry_v1.sec = timestampSec;
-            // And set the received bytes starting after the 28 bytes reserved for header.
-            std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
-            data->push_back(make_shared<LogEvent>(tmp));
+            data->push_back(make_shared<LogEvent>(it));
         }
         VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
         return true;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index c23d65e..5a0172b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -195,22 +195,13 @@
           new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
         // Disk Stats
         {android::util::DISK_STATS,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
         // Directory usage
         {android::util::DIRECTORY_USAGE,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
         // Size of app's code, data, and cache
         {android::util::APP_SIZE,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
          {{},
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index cf04ee3..f9f1b38 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,6 +41,44 @@
     }
 }
 
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+    mTagId = statsLogEventWrapper.getTagId();
+    mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
+    mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
+    mLogUid = 0;
+    for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
+        Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+        switch (statsLogEventWrapper.getElements()[i].type) {
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
+                mValues.push_back(
+                        FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
+                mValues.push_back(
+                        FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
+                mValues.push_back(FieldValue(
+                        field, Value(statsLogEventWrapper.getElements()[i].float_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
+                mValues.push_back(FieldValue(
+                        field, Value(statsLogEventWrapper.getElements()[i].double_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
+                mValues.push_back(
+                        FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
+                break;
+            case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
+                mValues.push_back(FieldValue(
+                        field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
+                break;
+            default:
+                break;
+        }
+    }
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
     mLogdTimestampNs = wallClockTimestampNs;
     mTagId = tagId;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 2ee6bdf..9ef0bf4 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
 
 #include "FieldValue.h"
 
+#include <android/os/StatsLogEventWrapper.h>
 #include <android/util/ProtoOutputStream.h>
 #include <log/log_event_list.h>
 #include <log/log_read.h>
@@ -61,6 +62,8 @@
      */
     explicit LogEvent(log_msg& msg);
 
+    explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+
     /**
      * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
      */
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a0ab3e4..805e583 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -322,6 +322,11 @@
                 case STRING:
                     protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
                     break;
+                case STORAGE:
+                    protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+                                       (const char*)dim.mValue.storage_value.data(),
+                                       dim.mValue.storage_value.size());
+                    break;
                 default:
                     break;
             }
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 950a258..455e4bb 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -84,7 +84,8 @@
                     cursor.close();
                 }
                 if (provider != null) {
-                    activityManager.removeContentProviderExternal(providerName, token);
+                    activityManager.removeContentProviderExternalAsUser(providerName, token,
+                            UserHandle.USER_SYSTEM);
                 }
             }
         } catch (RemoteException e) {
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index b683138..be9ccec 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -11067,6 +11067,7 @@
 HPLjava/lang/Long;->valueOf(Ljava/lang/String;I)Ljava/lang/Long;
 HPLjava/lang/Math;->addExact(JJ)J
 HPLjava/lang/Math;->scalb(FI)F
+HPLjava/lang/Object;->wait()V
 HPLjava/lang/StackTraceElement;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
 HPLjava/lang/String;->copyValueOf([C)Ljava/lang/String;
 HPLjava/lang/String;->join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 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/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 16360b3..2490cae 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -263,7 +263,9 @@
     void killAllBackgroundProcesses();
     ContentProviderHolder getContentProviderExternal(in String name, int userId,
             in IBinder token, String tag);
+    /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
     void removeContentProviderExternal(in String name, in IBinder token);
+    void removeContentProviderExternalAsUser(in String name, in IBinder token, int userId);
     // Get memory information about the calling process.
     void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
     boolean killProcessesBelowForeground(in String reason);
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4f172a4..e532ece 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -25,7 +25,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -214,7 +213,7 @@
     }
 
     /**
-     * Sets the night mode.
+     * Sets the system-wide night mode.
      * <p>
      * The mode can be one of:
      * <ul>
@@ -231,6 +230,12 @@
      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
      * device. Starting in API 23, changes to night mode are always effective.
+     * <p>
+     * Changes to night mode take effect globally and will result in a configuration change
+     * (and potentially an Activity lifecycle event) being applied to all running apps.
+     * Developers interested in an app-local implementation of night mode should consider using
+     * {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the
+     * -night qualifier locally.
      *
      * @param mode the night mode to set
      * @see #getNightMode()
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bd7a2dd..fc67c10 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3450,15 +3450,14 @@
      * @param flags Bit mask of additional options: currently supported flags are
      *            {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}.
      * @param reason a string that contains the reason for wiping data, which can be
-     *                          presented to the user.
+     *            presented to the user. If the string is null or empty, user won't be notified.
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
      * @throws IllegalArgumentException if the input reason string is null or empty.
      */
-    public void wipeData(int flags, @NonNull CharSequence reason) {
+    public void wipeData(int flags, CharSequence reason) {
         throwIfParentInstance("wipeData");
-        Preconditions.checkNotNull(reason, "CharSequence is null");
-        wipeDataInternal(flags, reason.toString());
+        wipeDataInternal(flags, reason != null ? reason.toString() : null);
     }
 
     /**
@@ -3469,7 +3468,7 @@
      * @see #wipeData(int, CharSequence)
      * @hide
      */
-    private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
+    private void wipeDataInternal(int flags, String wipeReasonForUser) {
         if (mService != null) {
             try {
                 mService.wipeDataWithReason(flags, wipeReasonForUser);
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 3fe17840..8760efe 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -95,9 +95,6 @@
      * Sets the current primary clip on the clipboard.  This is the clip that
      * is involved in normal cut and paste operations.
      *
-     * <em>If the application is not the default IME or does not have input focus this will have
-     * no effect.</em>
-     *
      * @param clip The clipped data item to set.
      * @see #getPrimaryClip()
      * @see #clearPrimaryClip()
@@ -115,9 +112,6 @@
     /**
      * Clears any current primary clip on the clipboard.
      *
-     * <em>If the application is not the default IME or does not have input focus this will have
-     * no effect.</em>
-     *
      * @see #setPrimaryClip(ClipData)
      */
     public void clearPrimaryClip() {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2ae3ae6..d88f6e3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3167,11 +3167,11 @@
      *
      * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
      * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
-     * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
-     * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
-     * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
-     * Generally, if you are running as an instant app you should always check whether the result
-     * of this method is null.
+     * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+     * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+     * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+     * return <code>null</code>.  Generally, if you are running as an instant app you should always
+     * check whether the result of this method is null.
      *
      * @param name The name of the desired service.
      *
@@ -3258,11 +3258,11 @@
      *
      * <p>Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true,
      * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE},
-     * {@link #FINGERPRINT_SERVICE}, {@link #SHORTCUT_SERVICE}, {@link #USB_SERVICE},
-     * {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, {@link #WIFI_SERVICE},
-     * {@link #WIFI_AWARE_SERVICE}. For these services this method will return <code>null</code>.
-     * Generally, if you are running as an instant app you should always check whether the result
-     * of this method is null.
+     * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE},
+     * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE},
+     * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will
+     * return <code>null</code>.  Generally, if you are running as an instant app you should always
+     * check whether the result of this method is null.
      *
      * @param serviceClass The class of the desired service.
      * @return The service or null if the class is not a supported system service.
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 963f881..19af609 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -102,7 +102,13 @@
 
     private static volatile boolean sForceSafeLabels = false;
 
-    /** {@hide} */
+    /**
+     * Always use {@link #loadSafeLabel safe labels} when calling {@link #loadLabel}.
+     *
+     * @param forceSafeLabels {@code true} to enforce safe labels
+     *
+     * @hide
+     */
     @SystemApi
     public static void setForceSafeLabels(boolean forceSafeLabels) {
         sForceSafeLabels = forceSafeLabels;
diff --git a/core/java/android/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/os/Binder.java b/core/java/android/os/Binder.java
index b9d9007..f947b5e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -773,6 +773,7 @@
             if (tracingEnabled) {
                 Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
             }
+            ThreadLocalWorkSourceUid.set(Binder.getCallingUid());
             res = onTransact(code, data, reply, flags);
         } catch (RemoteException|RuntimeException e) {
             if (observer != null) {
@@ -793,6 +794,7 @@
             }
             res = true;
         } finally {
+            ThreadLocalWorkSourceUid.clear();
             if (tracingEnabled) {
                 Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
             }
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index e03af9d..f3a9a50 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -739,6 +739,8 @@
 
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
         msg.target = this;
+        msg.workSourceUid = ThreadLocalWorkSourceUid.get();
+
         if (mAsynchronous) {
             msg.setAsynchronous(true);
         }
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index ded3a19..5b8abab 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -205,6 +205,7 @@
                 token = observer.messageDispatchStarting();
             }
             try {
+                ThreadLocalWorkSourceUid.set(msg.workSourceUid);
                 msg.target.dispatchMessage(msg);
                 if (observer != null) {
                     observer.messageDispatched(token, msg);
@@ -216,6 +217,7 @@
                 }
                 throw exception;
             } finally {
+                ThreadLocalWorkSourceUid.clear();
                 if (traceTag != 0) {
                     Trace.traceEnd(traceTag);
                 }
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 455d8ed..cd3f301 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import android.os.MessageProto;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -74,11 +73,25 @@
     public Messenger replyTo;
 
     /**
+     * Indicates that the uid is not set;
+     *
+     * @hide Only for use within the system server.
+     */
+    public static final int UID_NONE = -1;
+
+    /**
      * Optional field indicating the uid that sent the message.  This is
      * only valid for messages posted by a {@link Messenger}; otherwise,
      * it will be -1.
      */
-    public int sendingUid = -1;
+    public int sendingUid = UID_NONE;
+
+    /**
+     * Optional field indicating the uid that caused this message to be enqueued.
+     *
+     * @hide Only for use within the system server.
+     */
+    public int workSourceUid = UID_NONE;
 
     /** If set message is in use.
      * This flag is set when the message is enqueued and remains set while it
@@ -151,6 +164,7 @@
         m.obj = orig.obj;
         m.replyTo = orig.replyTo;
         m.sendingUid = orig.sendingUid;
+        m.workSourceUid = orig.workSourceUid;
         if (orig.data != null) {
             m.data = new Bundle(orig.data);
         }
@@ -301,7 +315,8 @@
         arg2 = 0;
         obj = null;
         replyTo = null;
-        sendingUid = -1;
+        sendingUid = UID_NONE;
+        workSourceUid = UID_NONE;
         when = 0;
         target = null;
         callback = null;
@@ -329,6 +344,7 @@
         this.obj = o.obj;
         this.replyTo = o.replyTo;
         this.sendingUid = o.sendingUid;
+        this.workSourceUid = o.workSourceUid;
 
         if (o.data != null) {
             this.data = (Bundle) o.data.clone();
@@ -612,6 +628,7 @@
         dest.writeBundle(data);
         Messenger.writeMessengerOrNullToParcel(replyTo, dest);
         dest.writeInt(sendingUid);
+        dest.writeInt(workSourceUid);
     }
 
     private void readFromParcel(Parcel source) {
@@ -625,5 +642,6 @@
         data = source.readBundle();
         replyTo = Messenger.readMessengerOrNullFromParcel(source);
         sendingUid = source.readInt();
+        workSourceUid = source.readInt();
     }
 }
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index b13bcac..051ab75 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -15,8 +15,10 @@
  */
 package android.os;
 
-import java.io.ByteArrayOutputStream;
-import java.nio.charset.StandardCharsets;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Wrapper class for sending data from Android OS to StatsD.
@@ -24,39 +26,28 @@
  * @hide
  */
 public final class StatsLogEventWrapper implements Parcelable {
-    private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
+    static final boolean DEBUG = false;
+    static final String TAG = "StatsLogEventWrapper";
 
-    // Below are constants copied from log/log.h
-    private static final int EVENT_TYPE_INT = 0;  /* int32_t */
-    private static final int EVENT_TYPE_LONG = 1; /* int64_t */
-    private static final int EVENT_TYPE_STRING = 2;
-    private static final int EVENT_TYPE_LIST = 3;
-    private static final int EVENT_TYPE_FLOAT = 4;
+    // Keep in sync with FieldValue.h enums
+    private static final int EVENT_TYPE_UNKNOWN = 0;
+    private static final int EVENT_TYPE_INT = 1; /* int32_t */
+    private static final int EVENT_TYPE_LONG = 2; /* int64_t */
+    private static final int EVENT_TYPE_FLOAT = 3;
+    private static final int EVENT_TYPE_DOUBLE = 4;
+    private static final int EVENT_TYPE_STRING = 5;
+    private static final int EVENT_TYPE_STORAGE = 6;
 
-    // Keep this in sync with system/core/logcat/event.logtags
-    private static final int STATS_BUFFER_TAG_ID = 1937006964;
-    /**
-     * Creates a log_event that is binary-encoded as implemented in
-     * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
-     * for pushed and pulled data. The write* methods must be called in the same order as their
-     * field number. There is no checking that the correct number of write* methods is called.
-     * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
-     * may be unnecessary.
-     *
-     * @param tag    The integer representing the tag for this event.
-     * @param fields The number of fields specified in this event.
-     */
-    public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
-        // Write four bytes from tag, starting with least-significant bit.
-        // For pulled data, this tag number is not really used. We use the same tag number as
-        // pushed ones to be consistent.
-        write4Bytes(STATS_BUFFER_TAG_ID);
-        mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
-        mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
-        // The first element is the elapsed realtime.
-        writeLong(elapsedNanos);
-        // The second element is the real atom tag number
-        writeInt(tag);
+    List<Integer> mTypes = new ArrayList<>();
+    List<Object> mValues = new ArrayList<>();
+    int mTag;
+    long mElapsedTimeNs;
+    long mWallClockTimeNs;
+
+    public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
+        this.mTag = tag;
+        this.mElapsedTimeNs = elapsedTimeNs;
+        this.mWallClockTimeNs = wallClockTimeNs;
     }
 
     /**
@@ -79,69 +70,80 @@
                 }
             };
 
-    private void write4Bytes(int val) {
-        mStorage.write(val);
-        mStorage.write(val >>> 8);
-        mStorage.write(val >>> 16);
-        mStorage.write(val >>> 24);
-    }
-
-    private void write8Bytes(long val) {
-        write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
-        write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
-    }
-
-    /**
-     * Adds 32-bit integer to output.
-     */
     public void writeInt(int val) {
-        mStorage.write(EVENT_TYPE_INT);
-        write4Bytes(val);
+        mTypes.add(EVENT_TYPE_INT);
+        mValues.add(val);
     }
 
-    /**
-     * Adds 64-bit long to output.
-     */
     public void writeLong(long val) {
-        mStorage.write(EVENT_TYPE_LONG);
-        write8Bytes(val);
+        mTypes.add(EVENT_TYPE_LONG);
+        mValues.add(val);
     }
 
     /**
-     * Adds a 4-byte floating point value to output.
-     */
-    public void writeFloat(float val) {
-        int v = Float.floatToIntBits(val);
-        mStorage.write(EVENT_TYPE_FLOAT);
-        write4Bytes(v);
-    }
-
-    /**
-     * Adds a string to the output.
+     * Write a string value.
      */
     public void writeString(String val) {
-        mStorage.write(EVENT_TYPE_STRING);
-        write4Bytes(val.length());
-        byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
-        mStorage.write(bytes, 0, bytes.length);
+        mTypes.add(EVENT_TYPE_STRING);
+        // use empty string for null
+        mValues.add(val == null ? "" : val);
+    }
+
+    public void writeFloat(float val) {
+        mTypes.add(EVENT_TYPE_FLOAT);
+        mValues.add(val);
     }
 
     /**
-     * Adds a boolean by adding either a 1 or 0 to the output.
+     * Write a storage value.
      */
+    public void writeStorage(byte[] val) {
+        mTypes.add(EVENT_TYPE_STORAGE);
+        mValues.add(val);
+    }
+
     public void writeBoolean(boolean val) {
-        int toWrite = val ? 1 : 0;
-        mStorage.write(EVENT_TYPE_INT);
-        write4Bytes(toWrite);
+        mTypes.add(EVENT_TYPE_INT);
+        mValues.add(val ? 1 : 0);
     }
 
     /**
      * Writes the stored fields to a byte array. Will first write a new-line character to denote
      * END_LIST before writing contents to byte array.
      */
+
     public void writeToParcel(Parcel out, int flags) {
-        mStorage.write(10); // new-line character is same as END_LIST
-        out.writeByteArray(mStorage.toByteArray());
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
+                            + mTypes.size() + " elements.");
+        }
+        out.writeInt(mTag);
+        out.writeLong(mElapsedTimeNs);
+        out.writeLong(mWallClockTimeNs);
+        out.writeInt(mTypes.size());
+        for (int i = 0; i < mTypes.size(); i++) {
+            out.writeInt(mTypes.get(i));
+            switch (mTypes.get(i)) {
+                case EVENT_TYPE_INT:
+                    out.writeInt((int) mValues.get(i));
+                    break;
+                case EVENT_TYPE_LONG:
+                    out.writeLong((long) mValues.get(i));
+                    break;
+                case EVENT_TYPE_FLOAT:
+                    out.writeFloat((float) mValues.get(i));
+                    break;
+                case EVENT_TYPE_STRING:
+                    out.writeString((String) mValues.get(i));
+                    break;
+                case EVENT_TYPE_STORAGE:
+                    out.writeByteArray((byte[]) mValues.get(i));
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/os/ThreadLocalWorkSourceUid.java b/core/java/android/os/ThreadLocalWorkSourceUid.java
new file mode 100644
index 0000000..df1d275
--- /dev/null
+++ b/core/java/android/os/ThreadLocalWorkSourceUid.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide Only for use within system server.
+ */
+public final class ThreadLocalWorkSourceUid {
+    public static final int UID_NONE = Message.UID_NONE;
+    private static final ThreadLocal<Integer> sWorkSourceUid =
+            ThreadLocal.withInitial(() -> UID_NONE);
+
+    /** Returns the original work source uid. */
+    public static int get() {
+        return sWorkSourceUid.get();
+    }
+
+    /** Sets the original work source uid. */
+    public static void set(int uid) {
+        sWorkSourceUid.set(uid);
+    }
+
+    /** Clears the stored work source uid. */
+    public static void clear() {
+        sWorkSourceUid.set(UID_NONE);
+    }
+
+    private ThreadLocalWorkSourceUid() {
+    }
+}
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index f2da21e..18aea03 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,6 +16,7 @@
 
 package android.permissionpresenterservice;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Context;
@@ -73,12 +74,13 @@
     public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
 
     /**
-     * Revoke the permission {@code permissionName} for app {@code packageName}
+     * Revokes the permission {@code permissionName} for app {@code packageName}
      *
      * @param packageName The package for which to revoke
      * @param permissionName The permission to revoke
      */
-    public abstract void onRevokeRuntimePermission(String packageName, String permissionName);
+    public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permissionName);
 
     @Override
     public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 1695c13..3893f2a 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.RemoteException;
+import android.util.Log;
 
 /**
  * <p><code>FillCallback</code> handles autofill requests from the {@link AutofillService} into
@@ -28,6 +29,9 @@
  * <a href="/guide/topics/text/autofill-services">Build autofill services</a>.
  */
 public final class FillCallback {
+
+    private static final String TAG = "FillCallback";
+
     private final IFillCallback mCallback;
     private final int mRequestId;
     private boolean mCalled;
@@ -39,13 +43,20 @@
     }
 
     /**
-     * Notifies the Android System that an
-     * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
-     * FillCallback)} was successfully fulfilled by the service.
+     * Notifies the Android System that a fill request
+     * ({@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+     * FillCallback)}) was successfully fulfilled by the service.
      *
-     * @param response autofill information for that activity, or {@code null} when the activity
-     * cannot be autofilled (for example, if it only contains read-only fields). See
-     * {@link FillResponse} for examples.
+     * <p>This method should always be called, even if the service doesn't have the heuristics to
+     * fulfill the request (in which case it should be called with {@code null}).
+     *
+     * <p>See the main {@link AutofillService} documentation for more details and examples.
+     *
+     * @param response autofill information for that activity, or {@code null} when the service
+     * cannot autofill the activity.
+     *
+     * @throws IllegalStateException if this method or {@link #onFailure(CharSequence)} was already
+     * called.
      */
     public void onSuccess(@Nullable FillResponse response) {
         assertNotCalled();
@@ -63,13 +74,28 @@
     }
 
     /**
-     * Notifies the Android System that an
+     * Notifies the Android System that a fill request (
      * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
-     * FillCallback)} could not be fulfilled by the service.
+     * FillCallback)}) could not be fulfilled by the service (for example, because the user data was
+     * not available yet), so the request could be retried later.
      *
-     * @param message error message to be displayed to the user.
+     * <p><b>Note: </b>this method should not be used when the service didn't have the heursitics to
+     * fulfill the request; in this case, the service should call {@link #onSuccess(FillResponse)
+     * onSuccess(null)} instead.
+     *
+     * <p><b>Note: </b>on Android versions up to {@link android.os.Build.VERSION_CODES#P}, this
+     * method is not working as intended, and the service should call
+     * {@link #onSuccess(FillResponse) onSuccess(null)} instead.
+     *
+     * @param message error message to be displayed to the user. <b>Note: </b> this message is
+     * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+     * Information, such as username or email address).
+     *
+     * @throws IllegalStateException if this method or {@link #onSuccess(FillResponse)} was already
+     * called.
      */
     public void onFailure(@Nullable CharSequence message) {
+        Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
         assertNotCalled();
         mCalled = true;
         try {
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 855981a..0625095 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.content.IntentSender;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
@@ -29,6 +30,9 @@
  * autofilled.
  */
 public final class SaveCallback {
+
+    private static final String TAG = "SaveCallback";
+
     private final ISaveCallback mCallback;
     private boolean mCalled;
 
@@ -41,6 +45,9 @@
      * Notifies the Android System that an
      * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled
      * by the service.
+     *
+     * @throws IllegalStateException if this method, {@link #onSuccess(IntentSender)}, or
+     * {@link #onFailure(CharSequence)} was already called.
      */
     public void onSuccess() {
         onSuccessInternal(null);
@@ -58,6 +65,9 @@
      *
      * @param intentSender intent that will be launched from the context of activity being
      * autofilled.
+     *
+     * @throws IllegalStateException if this method, {@link #onSuccess()},
+     * or {@link #onFailure(CharSequence)} was already called.
      */
     public void onSuccess(@NonNull IntentSender intentSender) {
         onSuccessInternal(Preconditions.checkNotNull(intentSender));
@@ -86,9 +96,15 @@
      * you prefer to show your own message, call {@link #onSuccess()} or
      * {@link #onSuccess(IntentSender)} instead.
      *
-     * @param message error message to be displayed to the user.
+     * @param message error message to be displayed to the user. <b>Note: </b> this message is
+     * displayed on {@code logcat} logs and should not contain PII (Personally Identifiable
+     * Information, such as username or email address).
+     *
+     * @throws IllegalStateException if this method, {@link #onSuccess()},
+     * or {@link #onSuccess(IntentSender)} was already called.
      */
     public void onFailure(CharSequence message) {
+        Log.w(TAG, "onFailure(): " + (message == null ? null : message.length() + "_chars"));
         assertNotCalled();
         mCalled = true;
         try {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 26240c5..c1a3c2b 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -283,6 +283,9 @@
                         } catch (android.os.RemoteException ex) {
                             Log.v(TAG, "Unable to contact notification manager", ex);
                             throw ex.rethrowFromSystemServer();
+                        } catch (SecurityException e) {
+                            // app cannot catch and recover from this, so do on their behalf
+                            Log.w(TAG, "Enqueue adjustment failed; no longer connected", e);
                         }
                     }
                     break;
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e3d68a6..24819a6 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -16,6 +16,8 @@
 
 package android.service.voice;
 
+import com.android.internal.app.IVoiceActionCheckCallback;
+
 /**
  * @hide
  */
@@ -24,4 +26,6 @@
     void soundModelsChanged();
     void shutdown();
     void launchVoiceAssistFromKeyguard();
+    void getActiveServiceSupportedActions(in List<String> voiceActions,
+     in IVoiceActionCheckCallback callback);
 }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0bbc07e..e105fdf 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,6 +16,8 @@
 
 package android.service.voice;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
@@ -26,17 +28,22 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 /**
  * Top-level service of the current global voice interactor, which is providing
@@ -71,23 +78,43 @@
     public static final String SERVICE_META_DATA = "android.voice_interaction";
 
     IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
-        @Override public void ready() {
-            mHandler.sendEmptyMessage(MSG_READY);
-        }
-        @Override public void shutdown() {
-            mHandler.sendEmptyMessage(MSG_SHUTDOWN);
-        }
-        @Override public void soundModelsChanged() {
-            mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
-        }
         @Override
-        public void launchVoiceAssistFromKeyguard() throws RemoteException {
-            mHandler.sendEmptyMessage(MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD);
+        public void ready() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onReady, VoiceInteractionService.this));
+        }
+
+        @Override
+        public void shutdown() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+        }
+
+        @Override
+        public void soundModelsChanged() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onSoundModelsChangedInternal,
+                    VoiceInteractionService.this));
+        }
+
+        @Override
+        public void launchVoiceAssistFromKeyguard() {
+            Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                    VoiceInteractionService::onLaunchVoiceAssistFromKeyguard,
+                    VoiceInteractionService.this));
+        }
+
+        @Override
+        public void getActiveServiceSupportedActions(List<String> voiceActions,
+                IVoiceActionCheckCallback callback) {
+            Handler.getMain().executeOrSendMessage(
+                    PooledLambda.obtainMessage(VoiceInteractionService::onHandleVoiceActionCheck,
+                            VoiceInteractionService.this,
+                            voiceActions,
+                            callback));
         }
     };
 
-    MyHandler mHandler;
-
     IVoiceInteractionManagerService mSystemService;
 
     private final Object mLock = new Object();
@@ -96,33 +123,6 @@
 
     private AlwaysOnHotwordDetector mHotwordDetector;
 
-    static final int MSG_READY = 1;
-    static final int MSG_SHUTDOWN = 2;
-    static final int MSG_SOUND_MODELS_CHANGED = 3;
-    static final int MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD = 4;
-
-    class MyHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_READY:
-                    onReady();
-                    break;
-                case MSG_SHUTDOWN:
-                    onShutdownInternal();
-                    break;
-                case MSG_SOUND_MODELS_CHANGED:
-                    onSoundModelsChangedInternal();
-                    break;
-                case MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD:
-                    onLaunchVoiceAssistFromKeyguard();
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    }
-
     /**
      * Called when a user has activated an affordance to launch voice assist from the Keyguard.
      *
@@ -186,7 +186,7 @@
      * be any combination of
      * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
      * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
-      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
+     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
      * to request that the system generate and deliver assist data on the current foreground
      * app as part of showing the session UI.
      */
@@ -200,10 +200,22 @@
         }
     }
 
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mHandler = new MyHandler();
+    /**
+     * Request to query for what extended voice actions this service supports. This method will
+     * be called when the system checks the supported actions of this
+     * {@link VoiceInteractionService}. Supported actions may be delivered to
+     * {@link VoiceInteractionSession} later to request a session to perform an action.
+     *
+     * <p>Voice actions are defined in support libraries and could vary based on platform context.
+     * For example, car related voice actions will be defined in car support libraries.
+     *
+     * @param voiceActions A set of checked voice actions.
+     * @return Returns a subset of checked voice actions. Additional voice actions in the
+     * returned set will be ignored. Returns null or empty set if no actions are supported.
+     */
+    @Nullable
+    public Set<String> onGetSupportedVoiceActions(@NonNull Set<String> voiceActions) {
+        return null;
     }
 
     @Override
@@ -254,6 +266,18 @@
         }
     }
 
+    private void onHandleVoiceActionCheck(List<String> voiceActions,
+            IVoiceActionCheckCallback callback) {
+        if (callback != null) {
+            try {
+                Set<String> voiceActionsSet = new ArraySet<>(voiceActions);
+                Set<String> resultSet = onGetSupportedVoiceActions(voiceActionsSet);
+                callback.onComplete(resultSet == null ? null : new ArrayList<>(resultSet));
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /**
      * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
      * This instance must be retained and used by the client.
@@ -289,12 +313,12 @@
     }
 
     /**
-      * Checks if a given keyphrase and locale are supported to create an
-      * {@link AlwaysOnHotwordDetector}.
-      *
-      * @return true if the keyphrase and locale combination is supported, false otherwise.
-      * @hide
-      */
+     * Checks if a given keyphrase and locale are supported to create an
+     * {@link AlwaysOnHotwordDetector}.
+     *
+     * @return true if the keyphrase and locale combination is supported, false otherwise.
+     * @hide
+     */
     @UnsupportedAppUsage
     public final boolean isKeyphraseAndLocaleSupportedForHotword(String keyphrase, Locale locale) {
         if (mKeyphraseEnrollmentInfo == null) {
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6edf845..6c15446 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -58,6 +58,6 @@
     /**
      * Just like {@link Paint#getTextRunCursor}.
      */
-    int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
+    int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
             int cursorOpt, Paint p);
 }
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 33c977b..c89617f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -619,7 +619,7 @@
                     previousLineBottom = lbottom;
                     int lbaseline = lbottom - getLineDescent(i);
 
-                    if (start >= spanEnd) {
+                    if (end >= spanEnd) {
                         // These should be infrequent, so we'll use this so that
                         // we don't have to check as often.
                         spanEnd = mLineBackgroundSpans.getNextTransition(start, textLength);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 9d841e8..c5fabaf 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1551,7 +1551,7 @@
      *
      * @param contextStart the start index of the context
      * @param contextEnd the (non-inclusive) end index of the context
-     * @param dir either DIRECTION_RTL or DIRECTION_LTR
+     * @param dir 1 if the run is RTL, otherwise 0
      * @param offset the cursor position to move from
      * @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
      * CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
@@ -1563,21 +1563,28 @@
     @Deprecated
     public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
             int cursorOpt, Paint p) {
+        return getTextRunCursor(contextStart, contextEnd, dir == 1, offset, cursorOpt, p);
+    }
+
+    /** @hide */
+    @Override
+    public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl, int offset,
+            int cursorOpt, Paint p) {
 
         int ret;
 
         int contextLen = contextEnd - contextStart;
         if (contextEnd <= mGapStart) {
             ret = p.getTextRunCursor(mText, contextStart, contextLen,
-                    dir, offset, cursorOpt);
+                    isRtl, offset, cursorOpt);
         } else if (contextStart >= mGapStart) {
             ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
-                    dir, offset + mGapLength, cursorOpt) - mGapLength;
+                    isRtl, offset + mGapLength, cursorOpt) - mGapLength;
         } else {
             char[] buf = TextUtils.obtain(contextLen);
             getChars(contextStart, contextEnd, buf, 0);
             ret = p.getTextRunCursor(buf, 0, contextLen,
-                    dir, offset - contextStart, cursorOpt) + contextStart;
+                    isRtl, offset - contextStart, cursorOpt) + contextStart;
             TextUtils.recycle(buf);
         }
 
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index ad7a851..b49a949 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -802,14 +802,13 @@
             }
         }
 
-        int dir = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
         int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
         if (mCharsValid) {
             return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
-                    dir, offset, cursorOpt);
+                    runIsRtl, offset, cursorOpt);
         } else {
             return wp.getTextRunCursor(mText, mStart + spanStart,
-                    mStart + spanLimit, dir, mStart + offset, cursorOpt) - mStart;
+                    mStart + spanLimit, runIsRtl, mStart + offset, cursorOpt) - mStart;
         }
     }
 
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 5f0a46d..d4bcd12 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -310,7 +310,7 @@
             return len;
         }
 
-        offset = paint.getTextRunCursor(text, offset, len, Paint.DIRECTION_LTR /* not used */,
+        offset = paint.getTextRunCursor(text, offset, len, false /* LTR, not used */,
                 offset, Paint.CURSOR_AFTER);
 
         return adjustReplacementSpan(text, offset, false /* move to the end */);
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 533d725..1203541 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,11 +410,11 @@
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
             SignatureInfo signatureInfo = findSignature(apk);
-            return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+            return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
         }
     }
 
-    static byte[] generateFsverityRootHash(String apkPath)
+    static byte[] generateApkVerityRootHash(String apkPath)
             throws IOException, SignatureNotFoundException, DigestException,
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -423,7 +423,7 @@
             if (vSigner.verityRootHash == null) {
                 return null;
             }
-            return ApkVerityBuilder.generateFsverityRootHash(
+            return ApkVerityBuilder.generateApkVerityRootHash(
                     apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
         }
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 758cd2b..939522d 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,11 +534,11 @@
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
             SignatureInfo signatureInfo = findSignature(apk);
-            return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+            return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
         }
     }
 
-    static byte[] generateFsverityRootHash(String apkPath)
+    static byte[] generateApkVerityRootHash(String apkPath)
             throws NoSuchAlgorithmException, DigestException, IOException,
                    SignatureNotFoundException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
@@ -547,7 +547,7 @@
             if (vSigner.verityRootHash == null) {
                 return null;
             }
-            return ApkVerityBuilder.generateFsverityRootHash(
+            return ApkVerityBuilder.generateApkVerityRootHash(
                     apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
         }
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index de9f55b..a299b11 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -432,16 +432,16 @@
      *
      * @return FSverity root hash
      */
-    public static byte[] generateFsverityRootHash(String apkPath)
+    public static byte[] generateApkVerityRootHash(String apkPath)
             throws NoSuchAlgorithmException, DigestException, IOException {
         // first try v3
         try {
-            return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
+            return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath);
         } catch (SignatureNotFoundException e) {
             // try older version
         }
         try {
-            return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
+            return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath);
         } catch (SignatureNotFoundException e) {
             return null;
         }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index e247c87..081033a 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -332,7 +332,7 @@
         try {
             byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
                     apk.length(), signatureInfo);
-            ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
+            ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk,
                     signatureInfo, new ByteBufferFactory() {
                         @Override
                         public ByteBuffer create(int capacity) {
@@ -348,26 +348,6 @@
     }
 
     /**
-     * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
-     * method does not check whether the root hash exists in the Signing Block or not.
-     *
-     * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
-     * ByteBufferFactory}.
-     *
-     * @return the root hash of the generated hash tree.
-     */
-    public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
-            SignatureInfo signatureInfo)
-            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
-                   NoSuchAlgorithmException {
-        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
-            ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
-                    signatureInfo, bufferFactory);
-            return result.rootHash;
-        }
-    }
-
-    /**
      * Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
      *
      * @throws IOException if an I/O error occurs while reading the file.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 2dd0117..553511d 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -16,6 +16,9 @@
 
 package android.util.apk;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
@@ -26,8 +29,10 @@
 import java.util.ArrayList;
 
 /**
- * ApkVerityBuilder builds the APK verity tree and the verity header, which will be used by the
- * kernel to verity the APK content on access.
+ * ApkVerityBuilder builds the APK verity tree and the verity header.  The generated tree format can
+ * be stored on disk for apk-verity setup and used by kernel.  Note that since the current
+ * implementation is different from the upstream, we call this implementation apk-verity instead of
+ * fs-verity.
  *
  * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
  * the existing APK format, it has to skip APK Signing Block and also has some special treatment for
@@ -47,26 +52,28 @@
     private static final byte[] DEFAULT_SALT = new byte[8];
 
     static class ApkVerityResult {
-        public final ByteBuffer fsverityData;
+        public final ByteBuffer verityData;
+        public final int merkleTreeSize;
         public final byte[] rootHash;
 
-        ApkVerityResult(ByteBuffer fsverityData, byte[] rootHash) {
-            this.fsverityData = fsverityData;
+        ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+            this.verityData = verityData;
+            this.merkleTreeSize = merkleTreeSize;
             this.rootHash = rootHash;
         }
     }
 
     /**
-     * Generates fsverity metadata and the Merkle tree into the {@link ByteBuffer} created by the
-     * {@link ByteBufferFactory}. The bytes layout in the buffer will be used by the kernel and is
-     * ready to be appended to the target file to set up fsverity. For fsverity to work, this data
-     * must be placed at the next page boundary, and the caller must add additional padding in that
-     * case.
+     * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
+     * ByteBuffer} created by the {@link ByteBufferFactory}.  The Merkle tree is suitable to be used
+     * as the on-disk format for apk-verity.
      *
-     * @return ApkVerityResult containing the fsverity data and the root hash of the Merkle tree.
+     * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+     *         front, the tree size, and the calculated root hash.
      */
-    static ApkVerityResult generateApkVerity(RandomAccessFile apk,
-            SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
+    @NonNull
+    static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+            @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
         long signingBlockSize =
                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
@@ -76,86 +83,69 @@
 
         ByteBuffer output = bufferFactory.create(
                 merkleTreeSize
-                + CHUNK_SIZE_BYTES);  // maximum size of fsverity metadata
+                + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
 
         ByteBuffer tree = slice(output, 0, merkleTreeSize);
-        ByteBuffer header = slice(output, merkleTreeSize,
-                merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
-                merkleTreeSize + CHUNK_SIZE_BYTES);
-        byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
-        ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
-        apkDigest.order(ByteOrder.LITTLE_ENDIAN);
+        byte[] apkRootHash = generateApkVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT,
+                levelOffset, tree);
+        return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
+    }
 
-        // NB: Buffer limit is set inside once finished.
-        calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
-
-        // Put the reverse offset to fs-verity header at the end.
-        output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
-        output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
-                + 4);  // size of this integer right before EOF
-        output.flip();
-
-        return new ApkVerityResult(output, apkDigestBytes);
+    static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
+            @NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput)
+            throws IOException {
+        footerOutput.order(ByteOrder.LITTLE_ENDIAN);
+        generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT);
+        long signingBlockSize =
+                signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+        generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset,
+                signingBlockSize, signatureInfo.eocdOffset);
     }
 
     /**
-     * Calculates the fsverity root hash for integrity measurement.  This needs to be consistent to
-     * what kernel returns.
+     * Calculates the apk-verity root hash for integrity measurement.  This needs to be consistent
+     * to what kernel returns.
      */
-    static byte[] generateFsverityRootHash(RandomAccessFile apk, ByteBuffer apkDigest,
-            SignatureInfo signatureInfo)
+    @NonNull
+    static byte[] generateApkVerityRootHash(@NonNull RandomAccessFile apk,
+            @NonNull ByteBuffer apkDigest, @NonNull SignatureInfo signatureInfo)
             throws NoSuchAlgorithmException, DigestException, IOException {
-        ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
-                .order(ByteOrder.LITTLE_ENDIAN);
-        ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
-                CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
+        assertSigningBlockAlignedAndHasFullPages(signatureInfo);
 
-        calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
+        ByteBuffer footer = ByteBuffer.allocate(CHUNK_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN);
+        generateApkVerityFooter(apk, signatureInfo, footer);
+        footer.flip();
 
         MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
-        md.update(header);
-        md.update(extensions);
+        md.update(footer);
         md.update(apkDigest);
         return md.digest();
     }
 
     /**
-     * Internal method to generate various parts of FSVerity constructs, including the header,
-     * extensions, Merkle tree, and the tree's root hash.  The output buffer is flipped to the
-     * generated data size and is readey for consuming.
+     * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This
+     * method does not check whether the root hash exists in the Signing Block or not.
+     *
+     * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+     * ByteBufferFactory}.
+     *
+     * @return the root hash of the generated hash tree.
      */
-    private static void calculateFsveritySignatureInternal(
-            RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
-            ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
-            throws IOException, NoSuchAlgorithmException, DigestException {
-        assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-        long signingBlockSize =
-                signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
-        long dataSize = apk.length() - signingBlockSize;
-        int[] levelOffset = calculateVerityLevelOffset(dataSize);
-
-        if (treeOutput != null) {
-            byte[] apkRootHash = generateApkVerityTree(apk, signatureInfo, DEFAULT_SALT,
-                    levelOffset, treeOutput);
-            if (rootHashOutput != null) {
-                rootHashOutput.put(apkRootHash);
-                rootHashOutput.flip();
-            }
-        }
-
-        if (headerOutput != null) {
-            headerOutput.order(ByteOrder.LITTLE_ENDIAN);
-            generateFsverityHeader(headerOutput, apk.length(), levelOffset.length - 1,
-                    DEFAULT_SALT);
-        }
-
-        if (extensionsOutput != null) {
-            extensionsOutput.order(ByteOrder.LITTLE_ENDIAN);
-            generateFsverityExtensions(extensionsOutput, signatureInfo.apkSigningBlockOffset,
-                    signingBlockSize, signatureInfo.eocdOffset);
+    @NonNull
+    static byte[] generateApkVerity(@NonNull String apkPath,
+            @NonNull ByteBufferFactory bufferFactory, @NonNull SignatureInfo signatureInfo)
+            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+                   NoSuchAlgorithmException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            ApkVerityResult result = generateApkVerityTree(apk, signatureInfo, bufferFactory);
+            ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
+                    result.verityData.limit());
+            generateApkVerityFooter(apk, signatureInfo, footer);
+            // Put the reverse offset to apk-verity header at the end.
+            footer.putInt(footer.position() + 4);
+            result.verityData.limit(result.merkleTreeSize + footer.position());
+            return result.rootHash;
         }
     }
 
@@ -297,9 +287,13 @@
         digester.fillUpLastOutputChunk();
     }
 
-    private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
-            byte[] salt, int[] levelOffset, ByteBuffer output)
+    @NonNull
+    private static byte[] generateApkVerityTreeInternal(@NonNull RandomAccessFile apk,
+            @Nullable SignatureInfo signatureInfo, @NonNull byte[] salt,
+            @NonNull int[] levelOffset, @NonNull ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
+        assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+
         // 1. Digest the apk to generate the leaf level hashes.
         generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
                     levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
@@ -324,7 +318,7 @@
         return rootHash;
     }
 
-    private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth,
+    private static ByteBuffer generateApkVerityHeader(ByteBuffer buffer, long fileSize,
             byte[] salt) {
         if (salt.length != 8) {
             throw new IllegalArgumentException("salt is not 8 bytes long");
@@ -351,13 +345,12 @@
         buffer.put(salt);                   // salt (8 bytes)
         skip(buffer, 22);                   // reserved
 
-        buffer.flip();
         return buffer;
     }
 
-    private static ByteBuffer generateFsverityExtensions(ByteBuffer buffer, long signingBlockOffset,
-            long signingBlockSize, long eocdOffset) {
-        // Snapshot of the FSVerity structs (subject to change once upstreamed).
+    private static ByteBuffer generateApkVerityExtensions(ByteBuffer buffer,
+            long signingBlockOffset, long signingBlockSize, long eocdOffset) {
+        // Snapshot of the experimental fs-verity structs (different from upstream).
         //
         // struct fsverity_extension_elide {
         //   __le64 offset;
@@ -409,7 +402,6 @@
             skip(buffer, kPadding);      // padding
         }
 
-        buffer.flip();
         return buffer;
     }
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bfe1e95..3bab87a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -17,8 +17,6 @@
 package android.view;
 
 import com.android.internal.os.IResultReceiver;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 
@@ -71,8 +69,7 @@
     boolean stopViewServer();            // Transaction #2
     boolean isViewServerRunning();       // Transaction #3
 
-    IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,
-            in IInputContext inputContext);
+    IWindowSession openSession(in IWindowSessionCallback callback);
 
     void getInitialDisplaySize(int displayId, out Point size);
     void getBaseDisplaySize(int displayId, out Point size);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79eafa8..7271a9e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -70,15 +70,8 @@
     private static native void nativeDestroy(long nativeObject);
     private static native void nativeDisconnect(long nativeObject);
 
-    private static native Bitmap nativeScreenshot(IBinder displayToken,
-            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
-            boolean allLayers, boolean useIdentityTransform, int rotation);
-    private static native GraphicBuffer nativeScreenshotToBuffer(IBinder displayToken,
-            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
-            boolean allLayers, boolean useIdentityTransform, int rotation);
-    private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
-            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
-            boolean allLayers, boolean useIdentityTransform);
+    private static native GraphicBuffer nativeScreenshot(IBinder displayToken,
+            Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation);
     private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
             Rect sourceCrop, float frameScale);
 
@@ -1189,52 +1182,39 @@
     }
 
     /**
-     * Copy the current screen contents into the provided {@link Surface}
-     *
-     * @param display The display to take the screenshot of.
-     * @param consumer The {@link Surface} to take the screenshot into.
-     * @param width The desired width of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param height The desired height of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param minLayer The lowest (bottom-most Z order) surface layer to
-     * include in the screenshot.
-     * @param maxLayer The highest (top-most Z order) surface layer to
-     * include in the screenshot.
-     * @param useIdentityTransform Replace whatever transformation (rotation,
-     * scaling, translation) the surface layers are currently using with the
-     * identity transformation while taking the screenshot.
-     */
-    public static void screenshot(IBinder display, Surface consumer,
-            int width, int height, int minLayer, int maxLayer,
-            boolean useIdentityTransform) {
-        screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer,
-                false, useIdentityTransform);
-    }
-
-    /**
-     * Copy the current screen contents into the provided {@link Surface}
-     *
-     * @param display The display to take the screenshot of.
-     * @param consumer The {@link Surface} to take the screenshot into.
-     * @param width The desired width of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param height The desired height of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     */
-    public static void screenshot(IBinder display, Surface consumer,
-            int width, int height) {
-        screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false);
-    }
-
-    /**
-     * Copy the current screen contents into the provided {@link Surface}
-     *
-     * @param display The display to take the screenshot of.
-     * @param consumer The {@link Surface} to take the screenshot into.
+     * @see SurfaceControl#screenshot(IBinder, Surface, Rect, int, int, boolean, int)
      */
     public static void screenshot(IBinder display, Surface consumer) {
-        screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false);
+        screenshot(display, consumer, new Rect(), 0, 0, false, 0);
+    }
+
+    /**
+     * Copy the current screen contents into the provided {@link Surface}
+     *
+     * @param consumer The {@link Surface} to take the screenshot into.
+     * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)
+     */
+    public static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width,
+            int height, boolean useIdentityTransform, int rotation) {
+        if (consumer == null) {
+            throw new IllegalArgumentException("consumer must not be null");
+        }
+
+        final GraphicBuffer buffer = screenshotToBuffer(display, sourceCrop, width, height,
+                useIdentityTransform, rotation);
+        try {
+            consumer.attachAndQueueBuffer(buffer);
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Failed to take screenshot - " + e.getMessage());
+        }
+    }
+
+    /**
+     * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
+     */
+    @UnsupportedAppUsage
+    public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
+        return screenshot(sourceCrop, width, height, false, rotation);
     }
 
     /**
@@ -1242,79 +1222,16 @@
      * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
      * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
      *
-     * CAVEAT: Versions of screenshot that return a {@link Bitmap} can
-     * be extremely slow; avoid use unless absolutely necessary; prefer
-     * the versions that use a {@link Surface} instead, such as
-     * {@link SurfaceControl#screenshot(IBinder, Surface)}.
+     * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
+     * unless absolutely necessary; prefer the versions that use a {@link Surface} such as
+     * {@link SurfaceControl#screenshot(IBinder, Surface)} or {@link GraphicBuffer} such as
+     * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
      *
-     * @param sourceCrop The portion of the screen to capture into the Bitmap;
-     * caller may pass in 'new Rect()' if no cropping is desired.
-     * @param width The desired width of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param height The desired height of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param minLayer The lowest (bottom-most Z order) surface layer to
-     * include in the screenshot.
-     * @param maxLayer The highest (top-most Z order) surface layer to
-     * include in the screenshot.
-     * @param useIdentityTransform Replace whatever transformation (rotation,
-     * scaling, translation) the surface layers are currently using with the
-     * identity transformation while taking the screenshot.
-     * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
-     * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
-     * screenshots in its native portrait orientation by default, so this is
-     * useful for returning screenshots that are independent of device
-     * orientation.
-     * @return Returns a hardware Bitmap containing the screen contents, or null
-     * if an error occurs. Make sure to call Bitmap.recycle() as soon as
-     * possible, once its content is not needed anymore.
+     * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
      */
     @UnsupportedAppUsage
     public static Bitmap screenshot(Rect sourceCrop, int width, int height,
-            int minLayer, int maxLayer, boolean useIdentityTransform,
-            int rotation) {
-        // TODO: should take the display as a parameter
-        IBinder displayToken = SurfaceControl.getBuiltInDisplay(
-                SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshot(displayToken, sourceCrop, width, height,
-                minLayer, maxLayer, false, useIdentityTransform, rotation);
-    }
-
-    /**
-     * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)}
-     * but returns a GraphicBuffer.
-     */
-    public static GraphicBuffer screenshotToBuffer(Rect sourceCrop, int width, int height,
-            int minLayer, int maxLayer, boolean useIdentityTransform,
-            int rotation) {
-        IBinder displayToken = SurfaceControl.getBuiltInDisplay(
-                SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshotToBuffer(displayToken, sourceCrop, width, height,
-                minLayer, maxLayer, false, useIdentityTransform, rotation);
-    }
-
-    /**
-     * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} but
-     * includes all Surfaces in the screenshot. This will also update the orientation so it
-     * sends the correct coordinates to SF based on the rotation value.
-     *
-     * @param sourceCrop The portion of the screen to capture into the Bitmap;
-     * caller may pass in 'new Rect()' if no cropping is desired.
-     * @param width The desired width of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param height The desired height of the returned bitmap; the raw
-     * screen will be scaled down to this size.
-     * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
-     * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take
-     * screenshots in its native portrait orientation by default, so this is
-     * useful for returning screenshots that are independent of device
-     * orientation.
-     * @return Returns a Bitmap containing the screen contents, or null
-     * if an error occurs. Make sure to call Bitmap.recycle() as soon as
-     * possible, once its content is not needed anymore.
-     */
-    @UnsupportedAppUsage
-    public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
+            boolean useIdentityTransform, int rotation) {
         // TODO: should take the display as a parameter
         IBinder displayToken = SurfaceControl.getBuiltInDisplay(
                 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
@@ -1323,22 +1240,45 @@
         }
 
         SurfaceControl.rotateCropForSF(sourceCrop, rotation);
-        return nativeScreenshot(displayToken, sourceCrop, width, height, 0, 0, true,
-                false, rotation);
+        final GraphicBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width, height,
+                useIdentityTransform, rotation);
+
+        if (buffer == null) {
+            Log.w(TAG, "Failed to take screenshot");
+            return null;
+        }
+        return Bitmap.createHardwareBitmap(buffer);
     }
 
-    @UnsupportedAppUsage
-    private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop,
-            int width, int height, int minLayer, int maxLayer, boolean allLayers,
-            boolean useIdentityTransform) {
+    /**
+     * Captures all the surfaces in a display and returns a {@link GraphicBuffer} with the content.
+     *
+     * @param display              The display to take the screenshot of.
+     * @param sourceCrop           The portion of the screen to capture into the Bitmap; caller may
+     *                             pass in 'new Rect()' if no cropping is desired.
+     * @param width                The desired width of the returned bitmap; the raw screen will be
+     *                             scaled down to this size; caller may pass in 0 if no scaling is
+     *                             desired.
+     * @param height               The desired height of the returned bitmap; the raw screen will
+     *                             be scaled down to this size; caller may pass in 0 if no scaling
+     *                             is desired.
+     * @param useIdentityTransform Replace whatever transformation (rotation, scaling, translation)
+     *                             the surface layers are currently using with the identity
+     *                             transformation while taking the screenshot.
+     * @param rotation             Apply a custom clockwise rotation to the screenshot, i.e.
+     *                             Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take
+     *                             screenshots in its native portrait orientation by default, so
+     *                             this is useful for returning screenshots that are independent of
+     *                             device orientation.
+     * @return Returns a GraphicBuffer that contains the captured content.
+     */
+    public static GraphicBuffer screenshotToBuffer(IBinder display, Rect sourceCrop, int width,
+            int height, boolean useIdentityTransform, int rotation) {
         if (display == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        if (consumer == null) {
-            throw new IllegalArgumentException("consumer must not be null");
-        }
-        nativeScreenshot(display, consumer, sourceCrop, width, height,
-                minLayer, maxLayer, allLayers, useIdentityTransform);
+
+        return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation);
     }
 
     private static void rotateCropForSF(Rect crop, int rot) {
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index e20acf1..92d145c3 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -196,7 +196,11 @@
         synchronized (WindowManagerGlobal.class) {
             if (sWindowSession == null) {
                 try {
-                    InputMethodManager imm = InputMethodManager.getInstance();
+                    if (InputMethodManager.ENABLE_LEGACY_EAGER_INITIALIZATION) {
+                        // Emulate the legacy behavior.  The global instance of InputMethodManager
+                        // was instantiated here.
+                        InputMethodManager.getInstance();
+                    }
                     IWindowManager windowManager = getWindowManagerService();
                     sWindowSession = windowManager.openSession(
                             new IWindowSessionCallback.Stub() {
@@ -204,8 +208,7 @@
                                 public void onAnimatorScaleChanged(float scale) {
                                     ValueAnimator.setDurationScale(scale);
                                 }
-                            },
-                            imm.getClient(), imm.getInputContext());
+                            });
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 02ada94..9ede57f 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;
@@ -226,6 +225,44 @@
 
     static final String PENDING_EVENT_COUNTER = "aq:imm";
 
+    /**
+     * {@code true} if we want to instantiate {@link InputMethodManager} eagerly in
+     * {@link android.view.WindowManagerGlobal#getWindowSession()}, which is often called in an
+     * early stage of process startup, which is how Android has worked.
+     *
+     * <p>We still have this settings because we know there are possible compatibility concerns if
+     * we stop doing so. Here are scenarios we know and there could be more scenarios we are not
+     * aware of right know.</p>
+     *
+     * <ul>
+     *     <li>Apps that directly access {@link #sInstance} via reflection, which is currently
+     *     allowed because of {@link UnsupportedAppUsage} annotation.  Currently
+     *     {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+     *     {@link #sInstance} is not {@code null} when such an app is accessing it, but removing
+     *     that code from {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal
+     *     untested code paths in their apps, which probably happen in an early startup time of that
+     *     app.</li>
+     *     <li>Apps that directly access {@link #peekInstance()} via reflection, which is currently
+     *     allowed because of {@link UnsupportedAppUsage} annotation.  Currently
+     *     {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that
+     *     {@link #peekInstance()} returns non-{@code null} object when such an app is calling
+     *     {@link #peekInstance()}, but removing that code from
+     *     {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal untested code
+     *     paths in their apps, which probably happen in an early startup time of that app. The good
+     *     news is that unlike {@link #sInstance}'s case we can at least work around this scenario
+     *     by changing the semantics of {@link #peekInstance()}, which is currently defined as
+     *     "retrieve the global {@link InputMethodManager} instance, if it exists" to something that
+     *     always returns non-{@code null} {@link InputMethodManager}.  However, introducing such an
+     *     workaround can also trigger different compatibility issues if {@link #peekInstance()} was
+     *     called before {@link android.view.WindowManagerGlobal#getWindowSession()} and it expected
+     *     {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li>
+     * </ul>
+     *
+     * <p>TODO(Bug 116157766): Check if we can set {@code false} here then remove this settings.</p>
+     * @hide
+     */
+    public static final boolean ENABLE_LEGACY_EAGER_INITIALIZATION = true;
+
     @UnsupportedAppUsage
     static InputMethodManager sInstance;
 
@@ -387,9 +424,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;
@@ -655,8 +689,10 @@
         synchronized (InputMethodManager.class) {
             if (sInstance == null) {
                 try {
-                    sInstance = new InputMethodManager(Looper.getMainLooper());
-                } catch (ServiceNotFoundException e) {
+                    final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper());
+                    imm.mService.addClient(imm.mClient, imm.mIInputContext);
+                    sInstance = imm;
+                } catch (ServiceNotFoundException | RemoteException e) {
                     throw new IllegalStateException(e);
                 }
             }
@@ -741,7 +777,7 @@
      */
     @Deprecated
     public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
-        mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
+        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
     }
 
     /**
@@ -751,7 +787,7 @@
      */
     @Deprecated
     public void hideStatusIcon(IBinder imeToken) {
-        mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
+        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
     }
 
     /** @hide */
@@ -1352,6 +1388,10 @@
                     mServedView.getWindowToken() == appWindowToken) {
                 finishInputLocked();
             }
+            if (mCurRootView != null &&
+                    mCurRootView.getWindowToken() == appWindowToken) {
+                mCurRootView = null;
+            }
         }
     }
 
@@ -1792,7 +1832,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);
@@ -1801,7 +1841,7 @@
             }
             return;
         }
-        mPrivOpsRegistry.get(token).setInputMethod(id);
+        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
     }
 
     /**
@@ -1821,7 +1861,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);
@@ -1830,7 +1870,7 @@
             }
             return;
         }
-        mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
+        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
     }
 
     /**
@@ -1850,7 +1890,7 @@
      */
     @Deprecated
     public void hideSoftInputFromInputMethod(IBinder token, int flags) {
-        mPrivOpsRegistry.get(token).hideMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
     }
 
     /**
@@ -1871,7 +1911,7 @@
      */
     @Deprecated
     public void showSoftInputFromInputMethod(IBinder token, int flags) {
-        mPrivOpsRegistry.get(token).showMySoftInput(flags);
+        InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
     }
 
     /**
@@ -2231,7 +2271,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);
@@ -2239,7 +2279,7 @@
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
     }
 
     /**
@@ -2259,7 +2299,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);
@@ -2267,7 +2307,8 @@
                 throw e.rethrowFromSystemServer();
             }
         }
-        return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+                .switchToNextInputMethod(onlyCurrentIme);
     }
 
     /**
@@ -2286,7 +2327,8 @@
      */
     @Deprecated
     public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
-        return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
+        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
+                .shouldOfferSwitchingToNextInputMethod();
     }
 
     /**
@@ -2422,34 +2464,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/TextView.java b/core/java/android/widget/TextView.java
index dfbaf9a..bbfac44 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12495,11 +12495,11 @@
                     advancesIndex);
         }
 
-        public int getTextRunCursor(int contextStart, int contextEnd, int dir,
+        public int getTextRunCursor(int contextStart, int contextEnd, boolean isRtl,
                 int offset, int cursorOpt, Paint p) {
             int contextCount = contextEnd - contextStart;
             return p.getTextRunCursor(mChars, contextStart + mStart,
-                    contextCount, dir, offset + mStart, cursorOpt);
+                    contextCount, isRtl, offset + mStart, cursorOpt);
         }
     }
 
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 9171959..0f8295a 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -16,8 +16,7 @@
 
 package com.android.internal.app;
 
-import com.android.internal.R;
-
+import android.annotation.NonNull;
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -32,6 +31,9 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Set;
+
 /**
  * Utility method for dealing with the assistant aspects of
  * {@link com.android.internal.app.IVoiceInteractionManagerService IVoiceInteractionManagerService}.
@@ -62,6 +64,30 @@
         return false;
     }
 
+    /**
+     * Checks the availability of a set of voice actions for the current active voice service.
+     *
+     * @param voiceActions A set of supported voice actions to be checked.
+     * @param callback     The callback which will deliver a set of supported voice actions. If
+     *                     no voice actions are supported for the given voice action set, then null
+     *                     or empty set is provided.
+     */
+    public void getActiveServiceSupportedActions(@NonNull Set<String> voiceActions,
+            @NonNull IVoiceActionCheckCallback callback) {
+        try {
+            if (mVoiceInteractionManagerService != null) {
+                mVoiceInteractionManagerService
+                        .getActiveServiceSupportedActions(new ArrayList<>(voiceActions), callback);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to call activeServiceSupportedActions", e);
+            try {
+                callback.onComplete(null);
+            } catch (RemoteException re) {
+            }
+        }
+    }
+
     public void launchVoiceAssistFromKeyguard() {
         try {
             if (mVoiceInteractionManagerService != null) {
@@ -157,7 +183,7 @@
             return getActiveServiceComponentName();
         }
         final SearchManager searchManager =
-            (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+                (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
         if (searchManager == null) {
             return null;
         }
diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
similarity index 63%
rename from packages/SystemUI/res/values-h600dp/config.xml
rename to core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
index 8616e3e..66ba93d 100644
--- a/packages/SystemUI/res/values-h600dp/config.xml
+++ b/core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl
@@ -1,22 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2017, The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
-*/
--->
+ */
 
-<resources>
-    <!-- The number of rows in the QuickSettings -->
-    <integer name="quick_settings_num_rows">3</integer>
-</resources>
+package com.android.internal.app;
+
+oneway interface IVoiceActionCheckCallback {
+    void onComplete(in List<String> voiceActions);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ff75a8b..5088cca 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -143,4 +144,11 @@
      * Register a voice interaction listener.
      */
     void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
+
+    /**
+     * Checks the availability of a set of voice actions for the current active voice service.
+     * Returns all supported voice actions.
+     */
+    void getActiveServiceSupportedActions(in List<String> voiceActions,
+     in IVoiceActionCheckCallback callback);
 }
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b33a5c4..81dab2f 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -232,6 +232,7 @@
         displayName = FileUtils.buildValidFatFilename(displayName);
 
         final File before = getFileForDocId(docId);
+        final File beforeVisibleFile = getFileForDocId(docId, true);
         final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
         if (!before.renameTo(after)) {
             throw new IllegalStateException("Failed to rename to " + after);
@@ -241,7 +242,6 @@
         onDocIdChanged(docId);
         onDocIdChanged(afterDocId);
 
-        final File beforeVisibleFile = getFileForDocId(docId, true);
         final File afterVisibleFile = getFileForDocId(afterDocId, true);
         moveInMediaStore(beforeVisibleFile, afterVisibleFile);
 
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
index 3255153..1436aed 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
@@ -29,12 +29,19 @@
 /**
  * A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
  * used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
+ *
+ * <p>This class is designed to be used as a per-process global registry.</p>
  */
 public final class InputMethodPrivilegedOperationsRegistry {
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
-            mRegistry = new WeakHashMap<>();
+    private InputMethodPrivilegedOperationsRegistry() {
+        // Not intended to be instantiated.
+    }
+
+    private static final Object sLock = new Object();
+
+    @Nullable
+    @GuardedBy("sLock")
+    private static WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>> sRegistry;
 
     @Nullable
     private static InputMethodPrivilegedOperations sNop;
@@ -62,10 +69,13 @@
      * @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
      */
     @AnyThread
-    public void put(IBinder token, InputMethodPrivilegedOperations ops) {
-        synchronized (mLock) {
+    public static void put(IBinder token, InputMethodPrivilegedOperations ops) {
+        synchronized (sLock) {
+            if (sRegistry == null) {
+                sRegistry = new WeakHashMap<>();
+            }
             final WeakReference<InputMethodPrivilegedOperations> previousOps =
-                    mRegistry.put(token, new WeakReference<>(ops));
+                    sRegistry.put(token, new WeakReference<>(ops));
             if (previousOps != null) {
                 throw new IllegalStateException(previousOps.get() + " is already registered for "
                         + " this token=" + token + " newOps=" + ops);
@@ -84,9 +94,12 @@
      */
     @NonNull
     @AnyThread
-    public InputMethodPrivilegedOperations get(IBinder token) {
-        synchronized (mLock) {
-            final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
+    public static InputMethodPrivilegedOperations get(IBinder token) {
+        synchronized (sLock) {
+            if (sRegistry == null) {
+                return getNopOps();
+            }
+            final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token);
             if (wrapperRef == null) {
                 return getNopOps();
             }
@@ -108,9 +121,15 @@
      * @param token IME token to be removed.
      */
     @AnyThread
-    public void remove(IBinder token) {
-        synchronized (mLock) {
-            mRegistry.remove(token);
+    public static void remove(IBinder token) {
+        synchronized (sLock) {
+            if (sRegistry == null) {
+                return;
+            }
+            sRegistry.remove(token);
+            if (sRegistry.isEmpty()) {
+                sRegistry = null;
+            }
         }
     }
 }
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 0650d0af..e4724ff 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -17,6 +17,7 @@
 package com.android.internal.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -36,7 +37,7 @@
  * @hide Only for use within the system server.
  */
 public class LooperStats implements Looper.Observer {
-    private static final int TOKEN_POOL_SIZE = 50;
+    private static final int SESSION_POOL_SIZE = 50;
 
     @GuardedBy("mLock")
     private final SparseArray<Entry> mEntries = new SparseArray<>(512);
@@ -78,17 +79,19 @@
         }
 
         DispatchSession session = (DispatchSession) token;
-        Entry entry = getOrCreateEntry(msg);
-        synchronized (entry) {
-            entry.messageCount++;
-            if (session != DispatchSession.NOT_SAMPLED) {
-                entry.recordedMessageCount++;
-                long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
-                long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
-                entry.totalLatencyMicro += latency;
-                entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
-                entry.cpuUsageMicro += cpuUsage;
-                entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+        Entry entry = findEntry(msg, /* allowCreateNew= */session != DispatchSession.NOT_SAMPLED);
+        if (entry != null) {
+            synchronized (entry) {
+                entry.messageCount++;
+                if (session != DispatchSession.NOT_SAMPLED) {
+                    entry.recordedMessageCount++;
+                    long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
+                    long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
+                    entry.totalLatencyMicro += latency;
+                    entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
+                    entry.cpuUsageMicro += cpuUsage;
+                    entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+                }
             }
         }
 
@@ -102,7 +105,7 @@
         }
 
         DispatchSession session = (DispatchSession) token;
-        Entry entry = getOrCreateEntry(msg);
+        Entry entry = findEntry(msg, /* allowCreateNew= */true);
         synchronized (entry) {
             entry.exceptionCount++;
         }
@@ -159,24 +162,28 @@
         mSamplingInterval = samplingInterval;
     }
 
-    @NonNull
-    private Entry getOrCreateEntry(Message msg) {
+    @Nullable
+    private Entry findEntry(Message msg, boolean allowCreateNew) {
         final boolean isInteractive = mDeviceState.isScreenInteractive();
         final int id = Entry.idFor(msg, isInteractive);
         Entry entry;
         synchronized (mLock) {
             entry = mEntries.get(id);
             if (entry == null) {
-                if (mEntries.size() >= mEntriesSizeCap) {
-                    // If over the size cap, track totals under a single entry.
+                if (!allowCreateNew) {
+                    return null;
+                } else if (mEntries.size() >= mEntriesSizeCap) {
+                    // If over the size cap track totals under OVERFLOW entry.
                     return mOverflowEntry;
+                } else {
+                    entry = new Entry(msg, isInteractive);
+                    mEntries.put(id, entry);
                 }
-                entry = new Entry(msg, isInteractive);
-                mEntries.put(id, entry);
             }
         }
 
-        if (entry.handler.getClass() != msg.getTarget().getClass()
+        if (entry.workSourceUid != msg.workSourceUid
+                || entry.handler.getClass() != msg.getTarget().getClass()
                 || entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread()
                 || entry.isInteractive != isInteractive) {
             // If a hash collision happened, track totals under a single entry.
@@ -186,7 +193,7 @@
     }
 
     private void recycleSession(DispatchSession session) {
-        if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) {
+        if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < SESSION_POOL_SIZE) {
             mSessionPool.add(session);
         }
     }
@@ -210,6 +217,7 @@
     }
 
     private static class Entry {
+        public final int workSourceUid;
         public final Handler handler;
         public final String messageName;
         public final boolean isInteractive;
@@ -222,12 +230,14 @@
         public long maxCpuUsageMicro;
 
         Entry(Message msg, boolean isInteractive) {
+            this.workSourceUid = msg.workSourceUid;
             this.handler = msg.getTarget();
             this.messageName = handler.getMessageName(msg);
             this.isInteractive = isInteractive;
         }
 
         Entry(String specialEntryName) {
+            this.workSourceUid = Message.UID_NONE;
             this.messageName = specialEntryName;
             this.handler = null;
             this.isInteractive = false;
@@ -245,6 +255,7 @@
 
         static int idFor(Message msg, boolean isInteractive) {
             int result = 7;
+            result = 31 * result + msg.workSourceUid;
             result = 31 * result + msg.getTarget().getLooper().getThread().hashCode();
             result = 31 * result + msg.getTarget().getClass().hashCode();
             result = 31 * result + (isInteractive ? 1231 : 1237);
@@ -258,6 +269,7 @@
 
     /** Aggregated data of Looper message dispatching in the in the current process. */
     public static class ExportedEntry {
+        public final int workSourceUid;
         public final String handlerClassName;
         public final String threadName;
         public final String messageName;
@@ -271,6 +283,7 @@
         public final long maxCpuUsageMicros;
 
         ExportedEntry(Entry entry) {
+            this.workSourceUid = entry.workSourceUid;
             if (entry.handler != null) {
                 this.handlerClassName = entry.handler.getClass().getName();
                 this.threadName = entry.handler.getLooper().getThread().getName();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 543f4a5..5f1243f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -31,6 +31,8 @@
  * applications.
  */
 interface IInputMethodManager {
+    void addClient(in IInputMethodClient client, in IInputContext inputContext);
+
     // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getInputMethodList();
     List<InputMethodInfo> getVrInputMethodList();
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 7548c22..101fd41 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -139,8 +139,7 @@
          * The client should try to restart input when its {@link android.view.Window} is focused
          * again.</p>
          *
-         * @see com.android.server.wm.WindowManagerInternal#inputMethodClientHasFocus(
-         * IInputMethodClient)
+         * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
          */
         int ERROR_NOT_IME_TARGET_WINDOW = 11;
         /**
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 743b9f6..b70177f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -157,23 +157,17 @@
     return Rect(left, top, right, bottom);
 }
 
-static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz,
+static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
         jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
-        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform,
-        int rotation) {
+        bool useIdentityTransform, int rotation) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken == NULL) {
         return NULL;
     }
     Rect sourceCrop = rectFromObj(env, sourceCropObj);
-    if (allLayers) {
-        minLayer = INT32_MIN;
-        maxLayer = INT32_MAX;
-    }
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::capture(displayToken,
-            sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform,
-            rotation, &buffer);
+    status_t res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
+            useIdentityTransform, rotation, &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -187,100 +181,6 @@
             (jlong)buffer.get());
 }
 
-static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
-        jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
-        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform,
-        int rotation) {
-    sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
-    if (displayToken == NULL) {
-        return NULL;
-    }
-
-    Rect sourceCrop = rectFromObj(env, sourceCropObj);
-
-    std::unique_ptr<ScreenshotClient> screenshot(new ScreenshotClient());
-    status_t res;
-    if (allLayers) {
-        minLayer = INT32_MIN;
-        maxLayer = INT32_MAX;
-    }
-
-    sp<GraphicBuffer> buffer;
-    res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
-        minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer);
-    if (res != NO_ERROR) {
-        return NULL;
-    }
-
-    SkColorType colorType;
-    SkAlphaType alphaType;
-
-    PixelFormat format = buffer->getPixelFormat();
-    switch (format) {
-        case PIXEL_FORMAT_RGBX_8888: {
-            colorType = kRGBA_8888_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        }
-        case PIXEL_FORMAT_RGBA_8888: {
-            colorType = kRGBA_8888_SkColorType;
-            alphaType = kPremul_SkAlphaType;
-            break;
-        }
-        case PIXEL_FORMAT_RGBA_FP16: {
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kPremul_SkAlphaType;
-            break;
-        }
-        case PIXEL_FORMAT_RGB_565: {
-            colorType = kRGB_565_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        }
-        default: {
-            return NULL;
-        }
-    }
-
-    SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
-                                         colorType, alphaType,
-                                         SkColorSpace::MakeSRGB());
-
-    auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
-    return bitmap::createBitmap(env, bitmap.release(),
-                                android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
-}
-
-static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
-        jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
-        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
-    sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
-    if (displayToken == NULL) {
-        return;
-    }
-
-    sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
-    if (consumer == NULL) {
-        return;
-    }
-
-    Rect sourceCrop;
-    if (sourceCropObj != NULL) {
-        sourceCrop = rectFromObj(env, sourceCropObj);
-    }
-
-    if (allLayers) {
-        minLayer = INT32_MIN;
-        maxLayer = INT32_MAX;
-    }
-
-    sp<GraphicBuffer> buffer;
-    ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer,
-                              useIdentityTransform, 0, &buffer);
-
-    Surface::attachAndQueueBuffer(consumer.get(), buffer);
-}
-
 static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
         jobject sourceCropObj, jfloat frameScale) {
 
@@ -919,10 +819,6 @@
             (void*)nativeDestroy },
     {"nativeDisconnect", "(J)V",
             (void*)nativeDisconnect },
-    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/Bitmap;",
-            (void*)nativeScreenshotBitmap },
-    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V",
-            (void*)nativeScreenshot },
     {"nativeCreateTransaction", "()J",
             (void*)nativeCreateTransaction },
     {"nativeApplyTransaction", "(JZ)V",
@@ -1013,9 +909,8 @@
             (void*)nativeDestroyInTransaction },
     {"nativeGetHandle", "(J)Landroid/os/IBinder;",
             (void*)nativeGetHandle },
-    {"nativeScreenshotToBuffer",
-     "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;",
-     (void*)nativeScreenshotToBuffer },
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
+            (void*)nativeScreenshot },
     {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
             (void*)nativeCaptureLayers },
 };
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 565a3ec..0c8dd9d 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
+import android.os.Message;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -74,14 +75,17 @@
     public void testSingleMessageDispatched() {
         TestableLooperStats looperStats = new TestableLooperStats(1, 100);
 
+        Message message = mHandlerFirst.obtainMessage(1000);
+        message.workSourceUid = 1000;
         Object token = looperStats.messageDispatchStarting();
         looperStats.tickRealtime(100);
         looperStats.tickThreadTime(10);
-        looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000));
+        looperStats.messageDispatched(token, message);
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
         assertThat(entries).hasSize(1);
         LooperStats.ExportedEntry entry = entries.get(0);
+        assertThat(entry.workSourceUid).isEqualTo(1000);
         assertThat(entry.threadName).isEqualTo("TestThread1");
         assertThat(entry.handlerClassName).isEqualTo(
                 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -100,15 +104,17 @@
     public void testThrewException() {
         TestableLooperStats looperStats = new TestableLooperStats(1, 100);
 
+        Message message = mHandlerFirst.obtainMessage(7);
+        message.workSourceUid = 123;
         Object token = looperStats.messageDispatchStarting();
         looperStats.tickRealtime(100);
         looperStats.tickThreadTime(10);
-        looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7),
-                new ArithmeticException());
+        looperStats.dispatchingThrewException(token, message, new ArithmeticException());
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
         assertThat(entries).hasSize(1);
         LooperStats.ExportedEntry entry = entries.get(0);
+        assertThat(entry.workSourceUid).isEqualTo(123);
         assertThat(entry.threadName).isEqualTo("TestThread1");
         assertThat(entry.handlerClassName).isEqualTo(
                 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -146,31 +152,39 @@
         looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
         }));
 
-        // Contributes to entry1.
+        // Will not be sampled so does not contribute to any entries.
         Object token4 = looperStats.messageDispatchStarting();
+        looperStats.tickRealtime(10);
+        looperStats.tickThreadTime(10);
+        looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(0));
+
+        // Contributes to entry1.
+        Object token5 = looperStats.messageDispatchStarting();
         looperStats.tickRealtime(100);
         looperStats.tickThreadTime(100);
-        looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1));
+        looperStats.messageDispatched(token5, mHandlerAnonymous.obtainMessage(1));
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
         assertThat(entries).hasSize(3);
         entries.sort(Comparator.comparing(e -> e.handlerClassName));
 
-        // Captures data for token4 call.
+        // Captures data for token5 call.
         LooperStats.ExportedEntry entry1 = entries.get(0);
+        assertThat(entry1.workSourceUid).isEqualTo(-1);
         assertThat(entry1.threadName).isEqualTo("TestThread1");
         assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
         assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
         assertThat(entry1.messageCount).isEqualTo(1);
-        assertThat(entry1.recordedMessageCount).isEqualTo(0);
+        assertThat(entry1.recordedMessageCount).isEqualTo(1);
         assertThat(entry1.exceptionCount).isEqualTo(0);
-        assertThat(entry1.totalLatencyMicros).isEqualTo(0);
-        assertThat(entry1.maxLatencyMicros).isEqualTo(0);
-        assertThat(entry1.cpuUsageMicros).isEqualTo(0);
-        assertThat(entry1.maxCpuUsageMicros).isEqualTo(0);
+        assertThat(entry1.totalLatencyMicros).isEqualTo(100);
+        assertThat(entry1.maxLatencyMicros).isEqualTo(100);
+        assertThat(entry1.cpuUsageMicros).isEqualTo(100);
+        assertThat(entry1.maxCpuUsageMicros).isEqualTo(100);
 
         // Captures data for token1 and token2 calls.
         LooperStats.ExportedEntry entry2 = entries.get(1);
+        assertThat(entry2.workSourceUid).isEqualTo(-1);
         assertThat(entry2.threadName).isEqualTo("TestThread1");
         assertThat(entry2.handlerClassName).isEqualTo(
                 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
@@ -185,6 +199,7 @@
 
         // Captures data for token3 call.
         LooperStats.ExportedEntry entry3 = entries.get(2);
+        assertThat(entry3.workSourceUid).isEqualTo(-1);
         assertThat(entry3.threadName).isEqualTo("TestThread2");
         assertThat(entry3.handlerClassName).isEqualTo(
                 "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
@@ -265,7 +280,7 @@
 
     @Test
     public void testMessagesOverSizeCap() {
-        TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */);
+        TestableLooperStats looperStats = new TestableLooperStats(1, 1 /* sizeCap */);
 
         Object token1 = looperStats.messageDispatchStarting();
         looperStats.tickRealtime(100);
@@ -296,12 +311,12 @@
         assertThat(entry1.handlerClassName).isEqualTo("");
         assertThat(entry1.messageName).isEqualTo("OVERFLOW");
         assertThat(entry1.messageCount).isEqualTo(3);
-        assertThat(entry1.recordedMessageCount).isEqualTo(1);
+        assertThat(entry1.recordedMessageCount).isEqualTo(3);
         assertThat(entry1.exceptionCount).isEqualTo(0);
-        assertThat(entry1.totalLatencyMicros).isEqualTo(10);
-        assertThat(entry1.maxLatencyMicros).isEqualTo(10);
-        assertThat(entry1.cpuUsageMicros).isEqualTo(10);
-        assertThat(entry1.maxCpuUsageMicros).isEqualTo(10);
+        assertThat(entry1.totalLatencyMicros).isEqualTo(70);
+        assertThat(entry1.maxLatencyMicros).isEqualTo(50);
+        assertThat(entry1.cpuUsageMicros).isEqualTo(40);
+        assertThat(entry1.maxCpuUsageMicros).isEqualTo(20);
 
         LooperStats.ExportedEntry entry2 = entries.get(1);
         assertThat(entry2.threadName).isEqualTo("TestThread1");
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 522db3c..b463dd8 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -21,8 +21,6 @@
 key 0x126    DPAD_DOWN
 key 0x127    DPAD_LEFT
 
-key 0x120    BUTTON_SELECT
-key 0x123    BUTTON_START
 key 0x12e    BUTTON_A
 key 0x12d    BUTTON_B
 key 0x12f    BUTTON_X
@@ -34,9 +32,6 @@
 key 0x121    BUTTON_THUMBL
 key 0x122    BUTTON_THUMBR
 
-# PS key
-key 0x2d0    BUTTON_MODE
-
 # Left Analog Stick
 axis 0x00    X
 axis 0x01    Y
@@ -74,3 +69,11 @@
 
 # Square
 # axis 0x37
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x120    BUTTON_SELECT
+# Start
+key 0x123    BUTTON_START
+# PS key
+key 0x2d0    BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+#   and when connected over Bluetooth
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+#   and when connected over Bluetooth
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
new file mode 100644
index 0000000..5fe35f7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when
+#   connected over USB
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -60,7 +60,6 @@
 key 0x138    BUTTON_SELECT
 # Options
 key 0x139    BUTTON_START
-
 # PS key
 key 0x13c    BUTTON_MODE
 
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+#   connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -60,7 +60,6 @@
 key 0x138    BUTTON_SELECT
 # Options
 key 0x139    BUTTON_START
-
 # PS key
 key 0x13c    BUTTON_MODE
 
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -0,0 +1,68 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+#   connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_0ba0.kl b/data/keyboards/Vendor_054c_Product_0ba0.kl
new file mode 100644
index 0000000..bc6fc3b
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0.kl
@@ -0,0 +1,70 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# L2 axis
+axis 0x03   LTRIGGER
+# R2 axis
+axis 0x04   RTRIGGER
+
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c    BUTTON_MODE
+
+# Touchpad press
+# The touchpad for this joystick will become a separate input device in future releases
+# and this button will be equivalent to left mouse button
+# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
+key 0x13d   BUTTON_1
diff --git a/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
new file mode 100644
index 0000000..8b85a38
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
@@ -0,0 +1,67 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+# - Version 0x8111 is for Linux hid-sony driver >=4.10
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 85b39fe..33caa00 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +38,8 @@
 
 import libcore.util.NativeAllocationRegistry;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -309,38 +312,47 @@
      */
     public static final int DIRECTION_RTL = 1;
 
+    /** @hide */
+    @IntDef(prefix = { "CURSOR_" }, value = {
+        CURSOR_AFTER, CURSOR_AT_OR_AFTER, CURSOR_BEFORE, CURSOR_AT_OR_BEFORE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CursorOption {}
+
     /**
-     * Option for getTextRunCursor to compute the valid cursor after
-     * offset or the limit of the context, whichever is less.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor after offset or the limit of the context, whichever is less.
      */
     public static final int CURSOR_AFTER = 0;
 
     /**
-     * Option for getTextRunCursor to compute the valid cursor at or after
-     * the offset or the limit of the context, whichever is less.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor at or after the offset or the limit of the context, whichever is
+     * less.
      */
     public static final int CURSOR_AT_OR_AFTER = 1;
 
      /**
-     * Option for getTextRunCursor to compute the valid cursor before
-     * offset or the start of the context, whichever is greater.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor before offset or the start of the context, whichever is greater.
      */
     public static final int CURSOR_BEFORE = 2;
 
    /**
-     * Option for getTextRunCursor to compute the valid cursor at or before
-     * offset or the start of the context, whichever is greater.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Compute the valid cursor at or before offset or the start of the context, whichever is
+     * greater.
      */
     public static final int CURSOR_AT_OR_BEFORE = 3;
 
     /**
-     * Option for getTextRunCursor to return offset if the cursor at offset
-     * is valid, or -1 if it isn't.
-     * @hide
+     * Option for getTextRunCursor.
+     *
+     * Return offset if the cursor at offset is valid, or -1 if it isn't.
      */
     public static final int CURSOR_AT = 4;
 
@@ -2410,34 +2422,32 @@
     }
 
     /**
-     * Returns the next cursor position in the run.  This avoids placing the
-     * cursor between surrogates, between characters that form conjuncts,
-     * between base characters and combining marks, or within a reordering
-     * cluster.
+     * Returns the next cursor position in the run.
      *
-     * <p>ContextStart and offset are relative to the start of text.
-     * The context is the shaping context for cursor movement, generally
-     * the bounds of the metric span enclosing the cursor in the direction of
-     * movement.
+     * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering cluster.
      *
-     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
-     * cursor position, this returns -1.  Otherwise this will never return a
-     * value before contextStart or after contextStart + contextLength.
+     * <p>
+     * ContextStart and offset are relative to the start of text.
+     * The context is the shaping context for cursor movement, generally the bounds of the metric
+     * span enclosing the cursor in the direction of movement.
+     *
+     * <p>
+     * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+     * returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextStart + contextLength.
      *
      * @param text the text
      * @param contextStart the start of the context
      * @param contextLength the length of the context
-     * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param isRtl true if the paragraph context is RTL, otherwise false
      * @param offset the cursor position to move from
-     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
-     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
-     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @param cursorOpt how to move the cursor
      * @return the offset of the next position, or -1
-     * @hide
      */
-    @UnsupportedAppUsage
-    public int getTextRunCursor(char[] text, int contextStart, int contextLength,
-            int dir, int offset, int cursorOpt) {
+    public int getTextRunCursor(@NonNull char[] text, @IntRange(from = 0) int contextStart,
+            @IntRange(from = 0) int contextLength, boolean isRtl, @IntRange(from = 0) int offset,
+            @CursorOption int cursorOpt) {
         int contextEnd = contextStart + contextLength;
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
@@ -2446,85 +2456,87 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength, dir, offset,
-                cursorOpt);
+        return nGetTextRunCursor(mNativePaint, text, contextStart, contextLength,
+                isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
     }
 
     /**
-     * Returns the next cursor position in the run.  This avoids placing the
-     * cursor between surrogates, between characters that form conjuncts,
-     * between base characters and combining marks, or within a reordering
-     * cluster.
+     * Returns the next cursor position in the run.
      *
-     * <p>ContextStart, contextEnd, and offset are relative to the start of
+     * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering cluster.
+     *
+     * <p>
+     * ContextStart, contextEnd, and offset are relative to the start of
      * text.  The context is the shaping context for cursor movement, generally
      * the bounds of the metric span enclosing the cursor in the direction of
      * movement.
      *
-     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
-     * cursor position, this returns -1.  Otherwise this will never return a
-     * value before contextStart or after contextEnd.
+     * <p>
+     * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+     * returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextEnd.
      *
      * @param text the text
      * @param contextStart the start of the context
      * @param contextEnd the end of the context
-     * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param isRtl true if the paragraph context is RTL, otherwise false
      * @param offset the cursor position to move from
-     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
-     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
-     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @param cursorOpt how to move the cursor
      * @return the offset of the next position, or -1
-     * @hide
      */
-    public int getTextRunCursor(CharSequence text, int contextStart,
-           int contextEnd, int dir, int offset, int cursorOpt) {
+    public int getTextRunCursor(@NonNull CharSequence text, @IntRange(from = 0) int contextStart,
+            @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+            @CursorOption int cursorOpt) {
 
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             return getTextRunCursor(text.toString(), contextStart, contextEnd,
-                    dir, offset, cursorOpt);
+                    isRtl, offset, cursorOpt);
         }
         if (text instanceof GraphicsOperations) {
             return ((GraphicsOperations) text).getTextRunCursor(
-                    contextStart, contextEnd, dir, offset, cursorOpt, this);
+                    contextStart, contextEnd, isRtl, offset, cursorOpt, this);
         }
 
         int contextLen = contextEnd - contextStart;
         char[] buf = TemporaryBuffer.obtain(contextLen);
         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
-        int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
+        int relPos = getTextRunCursor(buf, 0, contextLen, isRtl, offset - contextStart, cursorOpt);
         TemporaryBuffer.recycle(buf);
         return (relPos == -1) ? -1 : relPos + contextStart;
     }
 
     /**
-     * Returns the next cursor position in the run.  This avoids placing the
-     * cursor between surrogates, between characters that form conjuncts,
-     * between base characters and combining marks, or within a reordering
-     * cluster.
+     * Returns the next cursor position in the run.
      *
-     * <p>ContextStart, contextEnd, and offset are relative to the start of
-     * text.  The context is the shaping context for cursor movement, generally
-     * the bounds of the metric span enclosing the cursor in the direction of
-     * movement.
+     * This avoids placing the cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering cluster.
      *
-     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
-     * cursor position, this returns -1.  Otherwise this will never return a
-     * value before contextStart or after contextEnd.
+     * <p>
+     * ContextStart, contextEnd, and offset are relative to the start of text.  The context is the
+     * shaping context for cursor movement, generally the bounds of the metric span enclosing the
+     * cursor in the direction of movement.
+     * </p>
+     *
+     * <p>
+     * If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid cursor position, this
+     * returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextEnd.
+     * </p>
      *
      * @param text the text
      * @param contextStart the start of the context
      * @param contextEnd the end of the context
-     * @param dir either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param isRtl true if the paragraph context is RTL, otherwise false.
      * @param offset the cursor position to move from
-     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
-     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
-     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @param cursorOpt how to move the cursor
      * @return the offset of the next position, or -1
      * @hide
      */
-    public int getTextRunCursor(String text, int contextStart, int contextEnd,
-            int dir, int offset, int cursorOpt) {
+    public int getTextRunCursor(@NonNull String text, @IntRange(from = 0) int contextStart,
+            @IntRange(from = 0) int contextEnd, boolean isRtl, @IntRange(from = 0) int offset,
+            @CursorOption int cursorOpt) {
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
                 | (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2532,8 +2544,8 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd, dir, offset,
-                cursorOpt);
+        return nGetTextRunCursor(mNativePaint, text, contextStart, contextEnd,
+                isRtl ? DIRECTION_RTL : DIRECTION_LTR, offset, cursorOpt);
     }
 
     /**
@@ -2599,11 +2611,13 @@
      * Return in bounds (allocated by the caller) the smallest rectangle that
      * encloses all of the characters, with an implied origin at (0,0).
      *
+     * Note that styles are ignored even if you pass {@link android.text.Spanned} instance.
+     * Use {@link android.text.StaticLayout} for measuring bounds of {@link android.text.Spanned}.
+     *
      * @param text text to measure and return its bounds
      * @param start index of the first char in the text to measure
      * @param end 1 past the last char in the text to measure
      * @param bounds returns the unioned bounds of all the text. Must be allocated by the caller
-     * @hide
      */
     public void getTextBounds(CharSequence text, int start, int end, Rect bounds) {
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e6d2a6f..f2d50cd 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -146,7 +146,7 @@
                              frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
 
     // If we hit the deadline, cool!
-    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline || totalDuration < mFrameInterval) {
         if (isTripleBuffered) {
             mData->reportJankType(JankType::kHighInputLatency);
             (*mGlobalData)->reportJankType(JankType::kHighInputLatency);
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 255619c..52cb75e 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -25,6 +25,58 @@
 namespace android {
 namespace os {
 
+/**
+ * A wrapper for a union type to contain multiple types of values.
+ *
+ */
+struct StatsLogValue {
+  // Keep in sync with FieldValue.h
+  enum STATS_LOG_VALUE_TYPE {
+    UNKNOWN = 0,
+    INT = 1,
+    LONG = 2,
+    FLOAT = 3,
+    DOUBLE = 4,
+    STRING = 5,
+    STORAGE = 6
+  };
+
+  StatsLogValue() : type(UNKNOWN) {}
+
+  StatsLogValue(int32_t v) {
+    int_value = v;
+    type = INT;
+  }
+
+  StatsLogValue(int64_t v) {
+    long_value = v;
+    type = LONG;
+  }
+
+  StatsLogValue(float v) {
+    float_value = v;
+    type = FLOAT;
+  }
+
+  StatsLogValue(const std::string& v) {
+    str_value = v;
+    type = STRING;
+  }
+
+  void setType(STATS_LOG_VALUE_TYPE t) { type = t; }
+
+  union {
+    int32_t int_value;
+    int64_t long_value;
+    float float_value;
+    double double_value;
+  };
+  std::string str_value;
+  std::vector<uint8_t> storage_value;
+
+  STATS_LOG_VALUE_TYPE type;
+};
+
 // Represents a parcelable object. Only used to send data from Android OS to statsd.
 class StatsLogEventWrapper : public android::Parcelable {
  public:
@@ -36,8 +88,22 @@
 
   android::status_t readFromParcel(const android::Parcel* in);
 
-  // These are public for ease of conversion.
-  std::vector<uint8_t> bytes;
+  int getTagId() const { return mTagId; }
+
+  int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; }
+
+  int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
+
+  std::vector<StatsLogValue> getElements() const { return mElements; }
+
+ private:
+  int mTagId;
+
+  int64_t mElapsedRealTimeNs;
+
+  int64_t mWallClockTimeNs;
+
+  std::vector<StatsLogValue> mElements;
 };
 } // Namespace os
 } // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 8b3aa9a..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -32,13 +32,70 @@
 StatsLogEventWrapper::StatsLogEventWrapper(){};
 
 status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
-    out->writeByteVector(bytes);
-    return ::android::NO_ERROR;
+  // Implement me if desired. We don't currently use this.
+  ALOGE(
+      "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not "
+      "implemented.");
+  (void)out;  // To prevent compile error of unused parameter 'in'
+  return UNKNOWN_ERROR;
 };
 
 status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
-    in->readByteVector(&bytes);
-    return ::android::NO_ERROR;
+  status_t res = OK;
+  if (in == NULL) {
+    ALOGE("statsd received parcel argument was NULL.");
+    return BAD_VALUE;
+  }
+  if ((res = in->readInt32(&mTagId)) != OK) {
+    ALOGE("statsd could not read tagId from parcel");
+    return res;
+  }
+  if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) {
+    ALOGE("statsd could not read elapsed real time from parcel");
+    return res;
+  }
+  if ((res = in->readInt64(&mWallClockTimeNs)) != OK) {
+    ALOGE("statsd could not read wall clock time from parcel");
+    return res;
+  }
+  int dataSize = 0;
+  if ((res = in->readInt32(&dataSize)) != OK) {
+    ALOGE("statsd could not read data size from parcel");
+    return res;
+  }
+  if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 ||
+      dataSize <= 0) {
+    ALOGE("statsd received invalid parcel");
+    return BAD_VALUE;
+  }
+
+  for (int i = 0; i < dataSize; i++) {
+    int type = in->readInt32();
+    switch (type) {
+      case StatsLogValue::INT:
+        mElements.push_back(StatsLogValue(in->readInt32()));
+        break;
+      case StatsLogValue::LONG:
+        mElements.push_back(StatsLogValue(in->readInt64()));
+        break;
+      case StatsLogValue::STRING:
+        mElements.push_back(
+            StatsLogValue(std::string(String8(in->readString16()).string())));
+        break;
+      case StatsLogValue::FLOAT:
+        mElements.push_back(StatsLogValue(in->readFloat()));
+        break;
+      case StatsLogValue::STORAGE:
+        mElements.push_back(StatsLogValue());
+        mElements.back().setType(StatsLogValue::STORAGE);
+        in->readByteVector(&(mElements.back().storage_value));
+        break;
+      default:
+        ALOGE("unrecognized data type: %d", type);
+        return BAD_TYPE;
+    }
+  }
+  return NO_ERROR;
 };
 
 } // Namespace os
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 89827bc..7492aa6 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1108,12 +1108,9 @@
 
     /**
      * Sets playback rate using {@link PlaybackParams}. The object sets its internal
-     * PlaybackParams to the input, except that the object remembers previous speed
-     * when input speed is zero. This allows the object to resume at previous speed
-     * when play() is called. Calling it before the object is prepared does not change
-     * the object state. After the object is prepared, calling it with zero speed is
-     * equivalent to calling pause(). After the object is prepared, calling it with
-     * non-zero speed is equivalent to calling play().
+     * PlaybackParams to the input. This allows the object to resume at previous speed
+     * when play() is called. Speed of zero is not allowed. Calling it does not change
+     * the object state.
      *
      * @param params the playback params.
      */
diff --git a/media/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/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index 07f0c83..a8960d9 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -19,7 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tile_page"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:paddingStart="@dimen/notification_side_paddings"
     android:paddingEnd="@dimen/notification_side_paddings"
     android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index e96a09b..11a0187 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -19,6 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:layout_weight="1"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index adf1e74..2538612 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -37,7 +37,7 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:layout_gravity="center_vertical"
-            android:textColor="@android:color/white"
+            android:textColor="?attr/wallpaperTextColor"
             android:textSize="16sp"/>
         <ImageView
             android:id="@+id/dismiss"
@@ -49,6 +49,7 @@
             android:layout_marginEnd="2dp"
             android:alpha="0.7"
             android:src="@drawable/ic_close_white"
+            android:tint="?attr/wallpaperTextColor"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/accessibility_desc_close"/>
     </LinearLayout>
diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/packages/SystemUI/res/values-h320dp/config.xml
deleted file mode 100644
index a9c19db..0000000
--- a/packages/SystemUI/res/values-h320dp/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
--->
-
-<resources>
-    <!-- The number of rows in the QuickSettings -->
-    <integer name="quick_settings_num_rows">2</integer>
-</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 11bd392..24dcd3e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -107,7 +107,7 @@
     <integer name="quick_settings_num_columns">3</integer>
 
     <!-- The number of rows in the QuickSettings -->
-    <integer name="quick_settings_num_rows">1</integer>
+    <integer name="quick_settings_max_rows">3</integer>
 
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -146,19 +146,29 @@
     <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
     <bool name="config_hideLtePlus">false</bool>
 
-    <!-- milliseconds before the heads up notification auto-dismisses. -->
+    <!-- The number of milliseconds before the heads up notification auto-dismisses. -->
     <integer name="heads_up_notification_decay">5000</integer>
 
-    <!-- milliseconds after a heads up notification is pushed back
+    <!-- The number of milliseconds after a heads up notification is pushed back
      before the app can interrupt again. -->
     <integer name="heads_up_default_snooze_length_ms">60000</integer>
 
     <!-- Minimum display time for a heads up notification, in milliseconds. -->
     <integer name="heads_up_notification_minimum_time">2000</integer>
 
-    <!-- milliseconds before the heads up notification accepts touches. -->
+    <!-- The number of milliseconds before the heads up notification accepts touches. -->
     <integer name="touch_acceptance_delay">700</integer>
 
+    <!-- The number of milliseconds before the ambient notification auto-dismisses. This will
+         override the default pulse length. -->
+    <integer name="ambient_notification_decay">6000</integer>
+
+    <!-- Minimum display time for a heads up notification, in milliseconds. -->
+    <integer name="ambient_notification_minimum_time">2000</integer>
+
+    <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
+    <integer name="ambient_notification_extension_time">6000</integer>
+
     <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
     <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 04d72ce..258b6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,6 +30,7 @@
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -137,6 +138,7 @@
         providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
         providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
         providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
+        providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
         providers.put(NotificationBlockingHelperManager.class,
                 () -> new NotificationBlockingHelperManager(context));
         providers.put(NotificationRemoteInputManager.class,
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index d6a1cf0..5739c99 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -16,7 +16,6 @@
 package com.android.systemui.car;
 
 import android.content.Context;
-import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -41,7 +40,7 @@
     }
 
     @Override
-    public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
+    public boolean shouldHeadsUp(NotificationData.Entry entry) {
         // Because space is usually constrained in the auto use-case, there should not be a
         // pinned notification when the shade has been expanded. Ensure this by not pinning any
         // notification if the shade is already opened.
@@ -49,6 +48,6 @@
             return false;
         }
 
-        return super.shouldPeek(entry, sbn);
+        return super.shouldHeadsUp(entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 6a29299..bb05980 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -48,7 +48,15 @@
     void onIgnoreTouchWhilePulsing(boolean ignore);
 
     interface Callback {
-        default void onNotificationHeadsUp() {}
+        /**
+         * Called when a high priority notification is added.
+         */
+        default void onNotificationAlerted() {}
+
+        /**
+         * Called when battery state or power save mode changes.
+         * @param active whether power save is active or not
+         */
         default void onPowerSaveChanged(boolean active) {}
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1589969..31548b9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -409,7 +409,7 @@
 
     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
-        public void onNotificationHeadsUp() {
+        public void onNotificationAlerted() {
             onNotification();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1655c01..757c821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,9 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
+import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
+import static com.android.internal.telephony.IccCardConstants.State.READY;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -30,7 +33,6 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
-import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -38,6 +40,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricSourceType;
 import android.media.AudioManager;
 import android.media.SoundPool;
 import android.os.Bundle;
@@ -58,6 +61,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.ViewGroup;
 import android.view.WindowManagerPolicyConstants;
 import android.view.animation.Animation;
@@ -70,21 +74,21 @@
 import com.android.internal.policy.IKeyguardExitCallback;
 import com.android.internal.policy.IKeyguardStateCallback;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardDisplayManager;
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 
@@ -274,6 +278,12 @@
 
     private KeyguardUpdateMonitor mUpdateMonitor;
 
+    /**
+     * Last SIM state reported by the telephony system.
+     * Index is the slotId - in case of multiple SIM cards.
+     */
+    private final SparseArray<IccCardConstants.State> mLastSimStates = new SparseArray<>();
+
     private boolean mDeviceInteractive;
     private boolean mGoingToSleep;
 
@@ -450,6 +460,14 @@
                 }
             }
 
+            boolean simWasLocked;
+            synchronized (KeyguardViewMediator.this) {
+                IccCardConstants.State lastState = mLastSimStates.get(slotId);
+                simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED)
+                    && simState == READY;
+                mLastSimStates.append(slotId, simState);
+            }
+
             switch (simState) {
                 case NOT_READY:
                 case ABSENT:
@@ -503,6 +521,9 @@
                 case READY:
                     synchronized (KeyguardViewMediator.this) {
                         if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+                        if (mShowing && simWasLocked) {
+                            resetStateLocked();
+                        }
                         mLockWhenSimRemoved = true;
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 123fca7..fcd479c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,14 +6,12 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
+
 import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
@@ -41,14 +39,12 @@
         return t * t * t + 1.0f;
     };
 
-
     private final ArrayList<TileRecord> mTiles = new ArrayList<>();
     private final ArrayList<TilePage> mPages = new ArrayList<>();
 
     private PageIndicator mPageIndicator;
     private float mPageIndicatorPosition;
 
-    private int mNumPages;
     private PageListener mPageListener;
 
     private boolean mListening;
@@ -56,6 +52,7 @@
 
     private AnimatorSet mBounceAnimatorSet;
     private float mLastExpansion;
+    private boolean mDistributeTiles = false;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -122,7 +119,7 @@
 
     public void setPageIndicator(PageIndicator indicator) {
         mPageIndicator = indicator;
-        mPageIndicator.setNumPages(mNumPages);
+        mPageIndicator.setNumPages(mPages.size());
         mPageIndicator.setLocation(mPageIndicatorPosition);
     }
 
@@ -136,13 +133,15 @@
     @Override
     public void addTile(TileRecord tile) {
         mTiles.add(tile);
-        postDistributeTiles();
+        mDistributeTiles = true;
+        requestLayout();
     }
 
     @Override
     public void removeTile(TileRecord tile) {
         if (mTiles.remove(tile)) {
-            postDistributeTiles();
+            mDistributeTiles = true;
+            requestLayout();
         }
     }
 
@@ -175,44 +174,50 @@
         mPageListener = listener;
     }
 
-    private void postDistributeTiles() {
-        removeCallbacks(mDistribute);
-        post(mDistribute);
-    }
-
     private void distributeTiles() {
+        emptyAndInflateOrRemovePages();
+
+        final int tileCount = mPages.get(0).maxTiles();
         if (DEBUG) Log.d(TAG, "Distributing tiles");
-        final int NP = mPages.size();
-        for (int i = 0; i < NP; i++) {
-            mPages.get(i).removeAllViews();
-        }
         int index = 0;
         final int NT = mTiles.size();
         for (int i = 0; i < NT; i++) {
             TileRecord tile = mTiles.get(i);
-            if (mPages.get(index).isFull()) {
-                if (++index == mPages.size()) {
-                    if (DEBUG) Log.d(TAG, "Adding page for "
-                            + tile.tile.getClass().getSimpleName());
-                    mPages.add((TilePage) LayoutInflater.from(getContext())
-                            .inflate(R.layout.qs_paged_page, this, false));
-                }
+            if (mPages.get(index).mRecords.size() == tileCount) index++;
+            if (DEBUG) {
+                Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+                        + index);
             }
-            if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
-                    + index);
             mPages.get(index).addTile(tile);
         }
-        if (mNumPages != index + 1) {
-            mNumPages = index + 1;
-            while (mPages.size() > mNumPages) {
-                mPages.remove(mPages.size() - 1);
-            }
-            if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
-            mPageIndicator.setNumPages(mNumPages);
-            setAdapter(mAdapter);
-            mAdapter.notifyDataSetChanged();
-            setCurrentItem(0, false);
+    }
+
+    private void emptyAndInflateOrRemovePages() {
+        final int nTiles = mTiles.size();
+        int numPages = nTiles / mPages.get(0).maxTiles();
+        // Add one more not full page if needed
+        numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
+
+        final int NP = mPages.size();
+        for (int i = 0; i < NP; i++) {
+            mPages.get(i).removeAllViews();
         }
+        if (NP == numPages) {
+            return;
+        }
+        while (mPages.size() < numPages) {
+            if (DEBUG) Log.d(TAG, "Adding page");
+            mPages.add((TilePage) LayoutInflater.from(getContext())
+                    .inflate(R.layout.qs_paged_page, this, false));
+        }
+        while (mPages.size() > numPages) {
+            if (DEBUG) Log.d(TAG, "Removing page");
+            mPages.remove(mPages.size() - 1);
+        }
+        mPageIndicator.setNumPages(mPages.size());
+        setAdapter(mAdapter);
+        mAdapter.notifyDataSetChanged();
+        setCurrentItem(0, false);
     }
 
     @Override
@@ -222,20 +227,39 @@
         setPadding(0, 0, 0,
                 getContext().getResources().getDimensionPixelSize(
                         R.dimen.qs_paged_tile_layout_padding_bottom));
-
         boolean changed = false;
         for (int i = 0; i < mPages.size(); i++) {
             changed |= mPages.get(i).updateResources();
         }
         if (changed) {
-            distributeTiles();
+            mDistributeTiles = true;
+            requestLayout();
         }
         return changed;
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+        final int nTiles = mTiles.size();
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+
+            // Only change the pages if the number of rows or columns (from updateResources) has
+            // changed or the tiles have changed
+            if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+                mDistributeTiles = false;
+                distributeTiles();
+            }
+
+            final int nRows = mPages.get(0).mRows;
+            for (int i = 0; i < mPages.size(); i++) {
+                TilePage t = mPages.get(i);
+                t.mRows = nRows;
+            }
+        }
+
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
         // The ViewPager likes to eat all of the space, instead force it to wrap to the max height
         // of the pages.
         int maxHeight = 0;
@@ -249,13 +273,6 @@
         setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom());
     }
 
-    private final Runnable mDistribute = new Runnable() {
-        @Override
-        public void run() {
-            distributeTiles();
-        }
-    };
-
     public int getColumnCount() {
         if (mPages.size() == 0) return 0;
         return mPages.get(0).mColumns;
@@ -346,33 +363,17 @@
             };
 
     public static class TilePage extends TileLayout {
-        private int mMaxRows = 3;
+
         public TilePage(Context context, AttributeSet attrs) {
             super(context, attrs);
-            updateResources();
-        }
-
-        @Override
-        public boolean updateResources() {
-            final int rows = getRows();
-            boolean changed = rows != mMaxRows;
-            if (changed) {
-                mMaxRows = rows;
-                requestLayout();
-            }
-            return super.updateResources() || changed;
-        }
-
-        private int getRows() {
-            return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows));
-        }
-
-        public void setMaxRows(int maxRows) {
-            mMaxRows = maxRows;
         }
 
         public boolean isFull() {
-            return mRecords.size() >= mColumns * mMaxRows;
+            return mRecords.size() >= mColumns * mRows;
+        }
+
+        public int maxTiles() {
+            return mColumns * mRows;
         }
     }
 
@@ -401,7 +402,7 @@
 
         @Override
         public int getCount() {
-            return mNumPages;
+            return mPages.size();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index feff5d4..1451e71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -91,15 +92,24 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Since we control our own bottom, be whatever size we want.
-        // Otherwise the QSPanel ends up with 0 height when the window is only the
-        // size of the status bar.
-        mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
-                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+        // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
+        // bottom and footer are inside the screen.
+        Configuration config = getResources().getConfiguration();
+        boolean navBelow = config.smallestScreenWidthDp >= 600
+                || config.orientation != Configuration.ORIENTATION_LANDSCAPE;
+        MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+
+        // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
+        // subtract its height. We do not care if the collapsed notifications fit in the screen.
+        int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+                - getPaddingBottom();
+        if (navBelow) {
+            maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
+        }
+        mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
         int width = mQSPanel.getMeasuredWidth();
-        LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
         int height = layoutParams.topMargin + layoutParams.bottomMargin
-                + mQSPanel.getMeasuredHeight();
+                + mQSPanel.getMeasuredHeight() + getPaddingBottom();
         super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c67165e..01ff72e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -24,10 +24,12 @@
     protected int mCellMarginHorizontal;
     protected int mCellMarginVertical;
     protected int mSidePadding;
+    protected int mRows = 1;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     private int mCellMarginTop;
     private boolean mListening;
+    protected int mMaxAllowedRows = 3;
 
     public TileLayout(Context context) {
         this(context, null);
@@ -86,6 +88,7 @@
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
         mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
+        mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
         if (mColumns != columns) {
             mColumns = columns;
             requestLayout();
@@ -96,10 +99,16 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
+        // it will show all its tiles. In this case, the tiles have to be entered before the
+        // container is measured. Any change in the tiles, should trigger a remeasure.
         final int numTiles = mRecords.size();
         final int width = MeasureSpec.getSize(widthMeasureSpec)
                 - getPaddingStart() - getPaddingEnd();
-        final int numRows = (numTiles + mColumns - 1) / mColumns;
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightMode == MeasureSpec.UNSPECIFIED) {
+            mRows = (numTiles + mColumns - 1) / mColumns;
+        }
         mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
 
         // Measure each QS tile.
@@ -112,13 +121,35 @@
 
         // Only include the top margin in our measurement if we have more than 1 row to show.
         // Otherwise, don't add the extra margin buffer at top.
-        int height = (mCellHeight + mCellMarginVertical) * numRows +
-                (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
+        int height = (mCellHeight + mCellMarginVertical) * mRows +
+                (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
         if (height < 0) height = 0;
 
         setMeasuredDimension(width, height);
     }
 
+    /**
+     * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
+     * of 1 and a maximum of mMaxAllowedRows.
+     *
+     * @param heightMeasureSpec Available height.
+     * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
+     */
+    public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
+        final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop;
+        final int previousRows = mRows;
+        mRows = availableHeight / (mCellHeight + mCellMarginVertical);
+        if (mRows >= mMaxAllowedRows) {
+            mRows = mMaxAllowedRows;
+        } else if (mRows <= 1) {
+            mRows = 1;
+        }
+        if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+            mRows = (tilesCount + mColumns - 1) / mColumns;
+        }
+        return previousRows != mRows;
+    }
+
     @Override
     public boolean hasOverlappingRendering() {
         return false;
@@ -135,7 +166,8 @@
         int column = 0;
 
         // Layout each QS tile.
-        for (int i = 0; i < numRecords; i++, column++) {
+        final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
+        for (int i = 0; i < tilesToLayout; i++, column++) {
             // If we reached the last column available to layout a tile, wrap back to the next row.
             if (column == mColumns) {
                 column = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 4516518..b6e88d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -165,7 +165,7 @@
      * Whether or not the given notification is alerting and managed by this manager.
      * @return true if the notification is alerting
      */
-    public boolean contains(@NonNull String key) {
+    public boolean isAlerting(@NonNull String key) {
         return mAlertEntries.containsKey(key);
     }
 
@@ -294,7 +294,7 @@
             removeAutoRemovalCallbacks();
 
             if (!isSticky()) {
-                long finishTime = mPostTime + mAutoDismissNotificationDecay;
+                long finishTime = calculateFinishTime();
                 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
                 mHandler.postDelayed(mRemoveAlertRunnable, removeDelay);
             }
@@ -357,6 +357,14 @@
         protected long calculatePostTime() {
             return mClock.currentTimeMillis();
         }
+
+        /**
+         * Calculate when the notification should auto-dismiss itself.
+         * @return the finish time
+         */
+        protected long calculateFinishTime() {
+            return mPostTime + mAutoDismissNotificationDecay;
+        }
     }
 
     protected final static class Clock {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
new file mode 100644
index 0000000..2c384d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+
+/**
+ * Manager which handles high priority notifications that should "pulse" in when the device is
+ * dozing and/or in AOD.  The pulse uses the notification's ambient view and pops in briefly
+ * before automatically dismissing the alert.
+ */
+public final class AmbientPulseManager extends AlertingNotificationManager {
+
+    protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
+    @VisibleForTesting
+    protected long mExtensionTime;
+
+    public AmbientPulseManager(@NonNull final Context context) {
+        Resources resources = context.getResources();
+        mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay);
+        mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time);
+        mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
+    }
+
+    /**
+     * Adds an OnAmbientChangedListener to observe events.
+     */
+    public void addListener(@NonNull OnAmbientChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes the OnAmbientChangedListener from the observer list.
+     */
+    public void removeListener(@NonNull OnAmbientChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
+     * longer.
+     */
+    public void extendPulse() {
+        AmbientEntry topEntry = getTopEntry();
+        if (topEntry == null) {
+            return;
+        }
+        topEntry.extendPulse();
+    }
+
+    @Override
+    protected void onAlertEntryAdded(AlertEntry alertEntry) {
+        NotificationData.Entry entry = alertEntry.mEntry;
+        entry.row.setAmbientPulsing(true);
+        for (OnAmbientChangedListener listener : mListeners) {
+            listener.onAmbientStateChanged(entry, true);
+        }
+    }
+
+    @Override
+    protected void onAlertEntryRemoved(AlertEntry alertEntry) {
+        NotificationData.Entry entry = alertEntry.mEntry;
+        entry.row.setAmbientPulsing(false);
+        for (OnAmbientChangedListener listener : mListeners) {
+            listener.onAmbientStateChanged(entry, false);
+        }
+    }
+
+    @Override
+    protected AlertEntry createAlertEntry() {
+        return new AmbientEntry();
+    }
+
+    /**
+     * Get the top pulsing entry.  This should be the currently showing one if there are multiple.
+     * @return the currently showing entry
+     */
+    private AmbientEntry getTopEntry() {
+        if (mAlertEntries.isEmpty()) {
+            return null;
+        }
+        AlertEntry topEntry = null;
+        for (AlertEntry entry : mAlertEntries.values()) {
+            if (topEntry == null || entry.compareTo(topEntry) < 0) {
+                topEntry = entry;
+            }
+        }
+        return (AmbientEntry) topEntry;
+    }
+
+    /**
+     * Observer interface for any changes in the ambient entries.
+     */
+    public interface OnAmbientChangedListener {
+        /**
+         * Called when an entry starts or stops pulsing.
+         * @param entry the entry that changed
+         * @param isPulsing true if the entry is now pulsing, false otherwise
+         */
+        void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+    }
+
+    private final class AmbientEntry extends AlertEntry {
+        private boolean extended;
+
+        /**
+         * Extend the lifetime of the alertEntry so that it auto-removes later.  Can only be
+         * extended once.
+         */
+        private void extendPulse() {
+            if (!extended) {
+                extended = true;
+                updateEntry(false);
+            }
+        }
+
+        @Override
+        public void reset() {
+            super.reset();
+            extended = false;
+        }
+
+        @Override
+        protected long calculateFinishTime() {
+            return super.calculateFinishTime() + (extended ? mExtensionTime : 0);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f7615e6..d8f7b61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -121,7 +121,8 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class)
+                .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index d479838..f69ad43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -321,7 +321,7 @@
                         && !row.isLowPriority()));
             }
 
-            entry.row.setShowAmbient(mPresenter.isDozing());
+            entry.row.setOnAmbient(mPresenter.isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 7a69f2e..d6719f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -16,42 +16,70 @@
 
 package com.android.systemui.statusbar;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.util.ArraySet;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.Comparator;
 
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
 public class StatusBarStateController {
+    private static final String TAG = "SbStateController";
 
     private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
     private static final int MIN_STATE = StatusBarState.SHADE;
 
-    private final ArraySet<StateListener> mListeners = new ArraySet<>();
+    private static final Comparator <RankedListener> mComparator
+            = (o1, o2) -> Integer.compare(o1.rank, o2.rank);
+
+    private final ArrayList<RankedListener> mListeners = new ArrayList<>();
     private int mState;
     private int mLastState;
     private boolean mLeaveOpenOnKeyguardHide;
 
+    // TODO: b/115739177 (remove this explicit ordering if we can)
+    @Retention(SOURCE)
+    @IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF})
+    public @interface SbStateListenerRank {}
+    // This is the set of known dependencies when updating StatusBarState
+    public static final int RANK_STATUS_BAR = 0;
+    public static final int RANK_STATUS_BAR_WINDOW_CONTROLLER = 1;
+    public static final int RANK_STACK_SCROLLER = 2;
+    public static final int RANK_SHELF = 3;
+
     public int getState() {
         return mState;
     }
 
-    public void setState(int state) {
+    public boolean setState(int state) {
         if (state > MAX_STATE || state < MIN_STATE) {
             throw new IllegalArgumentException("Invalid state " + state);
         }
         if (state == mState) {
-            return;
+            return false;
         }
         synchronized (mListeners) {
-            for (StateListener listener : new ArraySet<>(mListeners)) {
-                listener.onStatePreChange(mState, state);
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onStatePreChange(mState, state);
             }
             mLastState = mState;
             mState = state;
-            for (StateListener listener : new ArraySet<>(mListeners)) {
-                listener.onStateChanged(mState);
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onStateChanged(mState);
+            }
+
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onStatePostChange();
             }
         }
+
+        return true;
     }
 
     public boolean goingToFullShade() {
@@ -72,20 +100,67 @@
 
     public void addListener(StateListener listener) {
         synchronized (mListeners) {
-            mListeners.add(listener);
+            addListenerInternalLocked(listener, Integer.MAX_VALUE);
         }
     }
 
+    /**
+     * Add a listener and a rank based on the priority of this message
+     * @param listener the listener
+     * @param rank the order in which you'd like to be called. Ranked listeners will be
+     * notified before unranked, and we will sort ranked listeners from low to high
+     *
+     * @deprecated This method exists only to solve latent inter-dependencies from refactoring
+     * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
+     * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+     */
+    public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+        synchronized (mListeners) {
+            addListenerInternalLocked(listener, rank);
+        }
+    }
+
+    @GuardedBy("mListeners")
+    private void addListenerInternalLocked(StateListener listener, int rank) {
+        // Protect against double-subscribe
+        for (RankedListener rl : mListeners) {
+            if (rl.listener.equals(listener)) {
+                return;
+            }
+        }
+
+        RankedListener rl = new RankedListener(listener, rank);
+        mListeners.add(rl);
+        mListeners.sort(mComparator);
+    }
+
     public void removeListener(StateListener listener) {
         synchronized (mListeners) {
-            mListeners.remove(listener);
+            mListeners.removeIf((it) -> it.listener.equals(listener));
         }
     }
 
+    public static String describe(int state) {
+        return StatusBarState.toShortString(state);
+    }
+
     public interface StateListener {
         public default void onStatePreChange(int oldState, int newState) {
         }
 
+        public default void onStatePostChange() {
+        }
+
         public void onStateChanged(int newState);
     }
+
+    private class RankedListener {
+        private final StateListener listener;
+        private final int rank;
+
+        private RankedListener(StateListener l, int r) {
+            listener = l;
+            rank = r;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 804e842..d097c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -440,6 +440,8 @@
             } else if (isHeadsUp) {
                 // Provide consistent ranking with headsUpManager
                 return mHeadsUpManager.compare(a, b);
+            } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
+                return a.row.isAmbientPulsing() ? -1 : 1;
             } else if (aMedia != bMedia) {
                 // Upsort current media notification.
                 return aMedia ? -1 : 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index ac01fa3..935eaac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -58,6 +58,8 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -91,7 +93,7 @@
         ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler,
         VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
-    protected static final boolean DEBUG = false;
+    protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     protected static final boolean ENABLE_HEADS_UP = true;
     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
@@ -121,6 +123,7 @@
             Dependency.get(ForegroundServiceController.class);
     protected final NotificationListener mNotificationListener =
             Dependency.get(NotificationListener.class);
+    protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
 
     protected IStatusBarService mBarService;
     protected NotificationPresenter mPresenter;
@@ -264,6 +267,7 @@
         }
 
         mNotificationLifetimeExtenders.add(mHeadsUpManager);
+        mNotificationLifetimeExtenders.add(mAmbientPulseManager);
         mNotificationLifetimeExtenders.add(mGutsManager);
         mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders());
 
@@ -381,7 +385,7 @@
         final int userId = n.getUserId();
         try {
             int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
-            if (isHeadsUp(n.getKey())) {
+            if (mHeadsUpManager.isAlerting(n.getKey())) {
                 dismissalSurface = NotificationStats.DISMISSAL_PEEK;
             } else if (mListContainer.hasPulsingNotifications()) {
                 dismissalSurface = NotificationStats.DISMISSAL_AOD;
@@ -432,12 +436,14 @@
     }
 
     private void addEntry(NotificationData.Entry shadeEntry) {
-        boolean isHeadsUped = shouldPeek(shadeEntry);
-        if (isHeadsUped) {
+        if (shouldHeadsUp(shadeEntry)) {
             mHeadsUpManager.showNotification(shadeEntry);
             // Mark as seen immediately
             setNotificationShown(shadeEntry.notification);
         }
+        if (shouldPulse(shadeEntry)) {
+            mAmbientPulseManager.showNotification(shadeEntry);
+        }
         addNotificationViews(shadeEntry);
         mCallback.onNotificationAdded(shadeEntry);
     }
@@ -465,7 +471,11 @@
     private void removeNotificationInternal(String key,
             @Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
         abortExistingInflation(key);
-        if (mHeadsUpManager.contains(key)) {
+
+        // Attempt to remove notifications from their alert managers (heads up, ambient pulse).
+        // Though the remove itself may fail, it lets the manager know to remove as soon as
+        // possible.
+        if (mHeadsUpManager.isAlerting(key)) {
             // A cancel() in response to a remote input shouldn't be delayed, as it makes the
             // sending look longer than it takes.
             // Also we should not defer the removal if reordering isn't allowed since otherwise
@@ -473,10 +483,11 @@
             boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
                     && !FORCE_REMOTE_INPUT_HISTORY
                     || !mVisualStabilityManager.isReorderingAllowed();
-
-            // Attempt to remove notification.
             mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
         }
+        if (mAmbientPulseManager.isAlerting(key)) {
+            mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+        }
 
         NotificationData.Entry entry = mNotificationData.get(key);
 
@@ -651,13 +662,15 @@
     private void addNotificationInternal(StatusBarNotification notification,
             NotificationListenerService.RankingMap rankingMap) throws InflationException {
         String key = notification.getKey();
-        if (DEBUG) Log.d(TAG, "addNotification key=" + key);
+        if (DEBUG) {
+            Log.d(TAG, "addNotification key=" + key);
+        }
 
         mNotificationData.updateRanking(rankingMap);
         NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
         rankingMap.getRanking(key, ranking);
         NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
-        boolean isHeadsUped = shouldPeek(shadeEntry);
+        boolean isHeadsUped = shouldHeadsUp(shadeEntry);
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
             if (shouldSuppressFullScreenIntent(shadeEntry)) {
                 if (DEBUG) {
@@ -750,7 +763,6 @@
             extender.setShouldManageLifetime(entry, false /* shouldManage */);
         }
 
-        Notification n = notification.getNotification();
         mNotificationData.updateRanking(ranking);
 
         final StatusBarNotification oldNotification = entry.notification;
@@ -763,10 +775,12 @@
         mForegroundServiceController.updateNotification(notification,
                 mNotificationData.getImportance(key));
 
-        boolean shouldPeek = shouldPeek(entry, notification);
-        boolean alertAgain = alertAgain(entry, n);
-
-        updateHeadsUp(key, entry, shouldPeek, alertAgain);
+        boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
+        if (mPresenter.isDozing()) {
+            updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
+        } else {
+            updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
+        }
         updateNotifications();
 
         if (!notification.isClearable()) {
@@ -851,66 +865,147 @@
         }
     }
 
-    protected boolean shouldPeek(NotificationData.Entry entry) {
-        return shouldPeek(entry, entry.notification);
-    }
+    /**
+     * Whether the notification should peek in from the top and alert the user.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should heads up, false otherwise
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
 
-    public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) {
-        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
-            if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
+        if (mPresenter.isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
+            }
             return false;
         }
 
-        if (mNotificationData.shouldFilterOut(entry)) {
-            if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
+        if (!canAlertCommon(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: no huns or vr mode");
+            }
             return false;
         }
 
         boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
 
-        if (!inUse && !mPresenter.isDozing()) {
+        if (!inUse) {
             if (DEBUG) {
-                Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
+                Log.d(TAG, "No heads up: not in use: " + sbn.getKey());
             }
             return false;
         }
 
-        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
-            return false;
-        }
-
-        // Peeking triggers an ambient display pulse, so disable peek is ambient is active
-        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
-            return false;
-        }
-
-        if (entry.hasJustLaunchedFullScreenIntent()) {
-            if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
+        if (mNotificationData.shouldSuppressPeek(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey());
+            }
             return false;
         }
 
         if (isSnoozedPackage(sbn)) {
-            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey());
+            }
             return false;
         }
 
-        // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
-        int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
-                : NotificationManager.IMPORTANCE_HIGH;
-        if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
-            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+        if (entry.hasJustLaunchedFullScreenIntent()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey());
+            }
             return false;
         }
 
-        // Don't peek notifications that are suppressed due to group alert behavior
+        if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!mCallback.canHeadsUp(entry, sbn)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Whether or not the notification should "pulse" on the user's display when the phone is
+     * dozing.  This displays the ambient view of the notification.
+     *
+     * @param entry the entry to check
+     * @return true if the entry should ambient pulse, false otherwise
+     */
+    protected boolean shouldPulse(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (!mPresenter.isDozing()) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (!canAlertCommon(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (mNotificationData.shouldSuppressAmbient(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (mNotificationData.getImportance(sbn.getKey())
+                < NotificationManager.IMPORTANCE_DEFAULT) {
+            if (DEBUG) {
+                Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Common checks between heads up alerting and ambient pulse alerting.  See
+     * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and
+     * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}.  Notifications that
+     * fail any of these checks should not alert at all.
+     *
+     * @param entry the entry to check
+     * @return true if these checks pass, false if the notification should not alert
+     */
+    protected boolean canAlertCommon(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        if (mNotificationData.shouldFilterOut(entry)) {
+            if (DEBUG) {
+                Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey());
+            }
+            return false;
+        }
+
+        // Don't alert notifications that are suppressed due to group alert behavior
         if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
-            if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
-            return false;
-        }
-
-        if (!mCallback.shouldPeek(entry, sbn)) {
+            if (DEBUG) {
+                Log.d(TAG, "No alerting: suppressed due to group alert behavior");
+            }
             return false;
         }
 
@@ -933,26 +1028,31 @@
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
 
-    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek,
-            boolean alertAgain) {
-        final boolean wasHeadsUp = isHeadsUp(key);
-        if (wasHeadsUp) {
-            if (!shouldPeek) {
+    /**
+     * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager}
+     * method.
+     * @param entry entry to update
+     * @param shouldAlert whether or not it should be alerting
+     * @param alertAgain whether or not an alert should actually come in as if it were new
+     * @param alertManager the alerting notification manager that manages the alert state
+     */
+    private void updateAlertState(NotificationData.Entry entry, boolean shouldAlert,
+            boolean alertAgain, AlertingNotificationManager alertManager) {
+        final boolean wasAlerting = alertManager.isAlerting(entry.key);
+        if (wasAlerting) {
+            if (!shouldAlert) {
                 // We don't want this to be interrupting anymore, lets remove it
-                mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
+                alertManager.removeNotification(entry.key,
+                        false /* ignoreEarliestRemovalTime */);
             } else {
-                mHeadsUpManager.updateNotification(entry.key, alertAgain);
+                alertManager.updateNotification(entry.key, alertAgain);
             }
-        } else if (shouldPeek && alertAgain) {
-            // This notification was updated to be a heads-up, show it!
-            mHeadsUpManager.showNotification(entry);
+        } else if (shouldAlert && alertAgain) {
+            // This notification was updated to be alerting, show it!
+            alertManager.showNotification(entry);
         }
     }
 
-    protected boolean isHeadsUp(String key) {
-        return mHeadsUpManager.contains(key);
-    }
-
     /**
      * Callback for NotificationEntryManager.
      */
@@ -1008,12 +1108,12 @@
         void onPerformRemoveNotification(StatusBarNotification statusBarNotification);
 
         /**
-         * Returns true if NotificationEntryManager should peek this notification.
+         * Returns true if NotificationEntryManager can heads up this notification.
          *
-         * @param entry entry of the notification that might be peeked
-         * @param sbn notification that might be peeked
-         * @return true if the notification should be peeked
+         * @param entry entry of the notification that might be heads upped
+         * @param sbn notification that might be heads upped
+         * @return true if the notification can be heads upped
          */
-        boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn);
+        boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
     }
 }
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/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 216ed68..019e88b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -174,6 +174,11 @@
      */
     private boolean mOnKeyguard;
 
+    /**
+     * Whether or not the row is currently on the doze screen.
+     */
+    private boolean mOnAmbient;
+
     private Animator mTranslateAnim;
     private ArrayList<View> mTranslateableViews;
     private NotificationContentView mPublicLayout;
@@ -186,7 +191,18 @@
     private NotificationData.Entry mEntry;
     private StatusBarNotification mStatusBarNotification;
     private String mAppName;
+
+    /**
+     * Whether or not the notification is using the heads up view and should peek from the top.
+     */
     private boolean mIsHeadsUp;
+
+    /**
+     * Whether or not the notification is using the ambient display view and is pulsing.  This
+     * occurs when a high priority notification alerts while the phone is dozing or is on AOD.
+     */
+    private boolean mIsAmbientPulsing;
+
     private boolean mLastChronometerRunning = true;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
@@ -289,7 +305,6 @@
     private float mContentTransformationAmount;
     private boolean mIconsVisible = true;
     private boolean mAboveShelf;
-    private boolean mShowAmbient;
     private boolean mIsLastChild;
     private Runnable mOnDismissRunnable;
     private boolean mIsLowPriority;
@@ -606,6 +621,14 @@
         }
     }
 
+    public boolean isAmbientPulsing() {
+        return mIsAmbientPulsing;
+    }
+
+    public void setAmbientPulsing(boolean isAmbientPulsing) {
+        mIsAmbientPulsing = isAmbientPulsing;
+    }
+
     public void setGroupManager(NotificationGroupManager groupManager) {
         mGroupManager = groupManager;
         mPrivateLayout.setGroupManager(groupManager);
@@ -1854,7 +1877,7 @@
     public void setDark(boolean dark, boolean fade, long delay) {
         super.setDark(dark, fade, delay);
         mDark = dark;
-        if (!mIsHeadsUp) {
+        if (!mIsAmbientPulsing) {
             // Only fade the showing view of the pulsing notification.
             fade = false;
         }
@@ -2155,7 +2178,7 @@
             return mPrivateLayout.getMinHeight();
         } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
             return getMinHeight();
-        } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) {
+        } else if (mIsSummaryWithChildren && (!mOnKeyguard || mOnAmbient)) {
             return mChildrenContainer.getIntrinsicHeight();
         } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) {
             if (isPinned() || mHeadsupDisappearRunning) {
@@ -2173,7 +2196,7 @@
     }
 
     private boolean isHeadsUpAllowed() {
-        return !mOnKeyguard && !mShowAmbient;
+        return !mOnKeyguard && !mOnAmbient;
     }
 
     @Override
@@ -2818,11 +2841,11 @@
                 || mExpandAnimationRunning || mChildIsExpanding);
     }
 
-    public void setShowAmbient(boolean showAmbient) {
-        if (showAmbient != mShowAmbient) {
-            mShowAmbient = showAmbient;
+    public void setOnAmbient(boolean onAmbient) {
+        if (onAmbient != mOnAmbient) {
+            mOnAmbient = onAmbient;
             if (mChildrenContainer != null) {
-                mChildrenContainer.notifyShowAmbientChanged();
+                mChildrenContainer.notifyDozingStateChanged();
             }
             notifyHeightChanged(false /* needsAnimation */);
         }
@@ -2891,8 +2914,8 @@
         return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f;
     }
 
-    public boolean isShowingAmbient() {
-        return mShowAmbient;
+    public boolean isOnAmbient() {
+        return mOnAmbient;
     }
 
     public void setAboveShelf(boolean aboveShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0110610..4963a0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -725,7 +725,7 @@
     }
 
     public int getMaxHeight() {
-        if (mContainingNotification.isShowingAmbient()) {
+        if (mContainingNotification.isOnAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (mExpandedChild != null) {
             return getViewHeight(VISIBLE_TYPE_EXPANDED)
@@ -752,7 +752,7 @@
     }
 
     public int getMinHeight(boolean likeGroupExpanded) {
-        if (mContainingNotification.isShowingAmbient()) {
+        if (mContainingNotification.isOnAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
             return getViewHeight(VISIBLE_TYPE_CONTRACTED);
@@ -1039,7 +1039,7 @@
      * @return one of the static enum types in this view, calculated form the current state
      */
     public int calculateVisibleType() {
-        if (mContainingNotification.isShowingAmbient()) {
+        if (mContainingNotification.isOnAmbient()) {
             if (mIsChildInGroup && mAmbientSingleLineChild != null) {
                 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
             } else if (mAmbientChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 15eaaac..8969aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -20,14 +20,15 @@
 import android.content.Context;
 import android.view.View;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
 
@@ -44,7 +45,7 @@
     private int mSpeedBumpIndex = -1;
     private boolean mDark;
     private boolean mHideSensitive;
-    private HeadsUpManager mHeadsUpManager;
+    private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
     private float mStackTranslation;
     private int mLayoutHeight;
     private int mTopPadding;
@@ -207,10 +208,6 @@
         mSpeedBumpIndex = shelfIndex;
     }
 
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
     public float getStackTranslation() {
         return mStackTranslation;
     }
@@ -334,10 +331,10 @@
     }
 
     public boolean isPulsing(NotificationData.Entry entry) {
-        if (!mPulsing || mHeadsUpManager == null) {
+        if (!mPulsing || mAmbientPulseManager == null) {
             return false;
         }
-        return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+        return mAmbientPulseManager.isAlerting(entry.key);
     }
 
     public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 3d44e3c..da089b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -213,7 +213,7 @@
             // calculated correctly as they are used to calculate how many we can fit on the screen.
             boolean isOverflow = i == overflowIndex;
             child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null &&
-                    !mContainingNotification.isShowingAmbient()
+                    !mContainingNotification.isOnAmbient()
                     ? mOverflowNumber.getMeasuredWidth() : 0);
             child.measure(widthMeasureSpec, newHeightSpec);
             // layout the divider
@@ -406,7 +406,7 @@
         if (childCount > maxAllowedVisibleChildren) {
             int number = childCount - maxAllowedVisibleChildren;
             mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
-            if (mContainingNotification.isShowingAmbient()) {
+            if (mContainingNotification.isOnAmbient()) {
                 ExpandableNotificationRow overflowView = mChildren.get(0);
                 HybridNotificationView ambientSingleLineView = overflowView == null ? null
                         : overflowView.getAmbientSingleLineView();
@@ -522,7 +522,7 @@
         if (mUserLocked) {
             expandFactor = getGroupExpandFraction();
         }
-        boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient();
+        boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isOnAmbient();
         for (int i = 0; i < childCount; i++) {
             if (visibleChildren >= maxAllowedVisibleChildren) {
                 break;
@@ -641,7 +641,7 @@
                     getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
             mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
 
-            if (mContainingNotification.isShowingAmbient()) {
+            if (mContainingNotification.isOnAmbient()) {
                 mGroupOverFlowState.alpha = 0.0f;
             } else if (!mChildrenExpanded) {
                 HybridNotificationView alignView = overflowView.getSingleLineView();
@@ -710,7 +710,7 @@
 
     @VisibleForTesting
     int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
-        if (mContainingNotification.isShowingAmbient()) {
+        if (mContainingNotification.isOnAmbient()) {
             return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
         }
         if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
@@ -900,7 +900,7 @@
         return mCurrentHeader;
     }
 
-    public void notifyShowAmbientChanged() {
+    public void notifyDozingStateChanged() {
         updateHeaderVisibility(false);
         updateGroupOverflow();
     }
@@ -970,7 +970,7 @@
 
     private ViewGroup calculateDesiredHeader() {
         ViewGroup desiredHeader;
-        if (mContainingNotification.isShowingAmbient()) {
+        if (mContainingNotification.isOnAmbient()) {
             desiredHeader = mNotificationHeaderAmbient;
         } else if (showingAsLowPriority()) {
             desiredHeader = mNotificationHeaderLowPriority;
@@ -1126,7 +1126,7 @@
     }
 
     public int getMinHeight() {
-        return getMinHeight(mContainingNotification.isShowingAmbient()
+        return getMinHeight(mContainingNotification.isOnAmbient()
                 ? NUMBER_OF_CHILDREN_WHEN_AMBIENT
                 : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
     }
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..958a162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -115,6 +115,8 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -134,8 +136,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.annotation.ShadeViewRefactor;
-import java.lang.annotation.ShadeViewRefactor.RefactorComponent;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -620,7 +620,8 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class)
+                .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
@@ -4248,8 +4249,9 @@
         mDimAnimator.addUpdateListener(mDimUpdateListener);
         mDimAnimator.start();
     }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setHideSensitive(boolean hideSensitive, boolean animate) {
+    private void setHideSensitive(boolean hideSensitive, boolean animate) {
         if (hideSensitive != mAmbientState.isHideSensitive()) {
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
@@ -4826,7 +4828,6 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
-        mAmbientState.setHeadsUpManager(headsUpManager);
         mHeadsUpManager.addListener(mRoundnessManager);
         mHeadsUpManager.setAnimationStateHandler(this);
     }
@@ -5008,8 +5009,12 @@
     private void setStatusBarState(int statusBarState) {
         mStatusBarState = statusBarState;
         mAmbientState.setStatusBarState(statusBarState);
+    }
+
+    private void onStatePostChange() {
         boolean onKeyguard = onKeyguard();
         boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+
         if (mHeadsUpAppearanceController != null) {
             mHeadsUpAppearanceController.setPublicMode(publicMode);
         }
@@ -5026,6 +5031,8 @@
         updateFooter();
         updateChildren();
         onUpdateRowStates();
+
+        mEntryManager.updateNotifications();
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5949,5 +5956,10 @@
         public void onStateChanged(int newState) {
             setStatusBarState(newState);
         }
-    };
+
+      @Override
+      public void onStatePostChange() {
+          NotificationStackScrollLayout.this.onStatePostChange();
+      }
+  };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 0d3ba77..25db4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -63,9 +63,14 @@
             if (!mDozing) {
                 return;
             }
-            mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
-            mHandler.postDelayed(mPulseOutExtended,
-                    mDozeParameters.getPulseVisibleDurationExtended());
+            // All pulses except notifications should time out on their own.  Pulses due to
+            // notifications should instead be managed externally based off the notification's
+            // lifetime.
+            if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) {
+                mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+                mHandler.postDelayed(mPulseOutExtended,
+                        mDozeParameters.getPulseVisibleDurationExtended());
+            }
             mFullyPulsing = true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4a05989..cfc3271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -177,7 +177,7 @@
             mReleaseOnExpandFinish = false;
         } else {
             for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
-                if (contains(entry.key)) {
+                if (isAlerting(entry.key)) {
                     // Maybe the heads-up was removed already
                     removeAlertEntry(entry.key);
                 }
@@ -345,7 +345,7 @@
     public void onReorderingAllowed() {
         mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
         for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
-            if (contains(entry.key)) {
+            if (isAlerting(entry.key)) {
                 // Maybe the heads-up was removed already
                 removeAlertEntry(entry.key);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 6b6566c..c08366a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
@@ -23,6 +24,9 @@
 import android.util.Log;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -43,15 +47,18 @@
 /**
  * A class to handle notifications and their corresponding groups.
  */
-public class NotificationGroupManager implements OnHeadsUpChangedListener {
+public class NotificationGroupManager implements OnHeadsUpChangedListener,
+        OnAmbientChangedListener {
 
     private static final String TAG = "NotificationGroupManager";
-    private static final long HEADS_UP_TRANSFER_TIMEOUT = 300;
+    private static final long ALERT_TRANSFER_TIMEOUT = 300;
     private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
     private OnGroupChangeListener mListener;
     private int mBarState = -1;
     private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
     private HeadsUpManager mHeadsUpManager;
+    private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+    private boolean mIsDozing;
     private boolean mIsUpdatingUnchangedGroup;
     private HashMap<String, NotificationData.Entry> mPendingNotifications;
 
@@ -162,40 +169,58 @@
                 mListener.onGroupCreatedFromChildren(group);
             }
         }
-        cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */);
+        cleanUpAlertStatesOnAdd(group, false /* addIsPending */);
     }
 
     public void onPendingEntryAdded(NotificationData.Entry shadeEntry) {
         String groupKey = getGroupKey(shadeEntry.notification);
         NotificationGroup group = mGroupMap.get(groupKey);
         if (group != null) {
-            cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */);
+            cleanUpAlertStatesOnAdd(group, true /* addIsPending */);
         }
     }
 
     /**
-     * Clean up the heads up states when a new child was added.
+     * Set whether or not the device is dozing.  This allows the group manager to reset some
+     * specific alert state logic based off when the state changes.
+     * @param isDozing if the device is dozing.
+     */
+    public void setDozing(boolean isDozing) {
+        if (mIsDozing != isDozing) {
+            for (NotificationGroup group : mGroupMap.values()) {
+                group.lastAlertTransfer = 0;
+                group.alertSummaryOnNextAddition = false;
+            }
+        }
+        mIsDozing = isDozing;
+    }
+
+    /**
+     * Clean up the alert states when a new child was added.
      * @param group The group where a view was added or will be added.
      * @param addIsPending True if is the addition still pending or false has it already been added.
      */
-    private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) {
-        if (!addIsPending && group.hunSummaryOnNextAddition) {
-            if (!mHeadsUpManager.contains(group.summary.key)) {
-                mHeadsUpManager.showNotification(group.summary);
+    private void cleanUpAlertStatesOnAdd(NotificationGroup group, boolean addIsPending) {
+
+        AlertingNotificationManager alertManager =
+                mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
+        if (!addIsPending && group.alertSummaryOnNextAddition) {
+            if (!alertManager.isAlerting(group.summary.key)) {
+                alertManager.showNotification(group.summary);
             }
-            group.hunSummaryOnNextAddition = false;
+            group.alertSummaryOnNextAddition = false;
         }
         // Because notification groups are not delivered as a whole unit, it may happen that a
         // group child gets added quite a bit after the summary got posted. Our guidance is, that
         // apps should always post the group summary as well and we'll hide it for them if the child
-        // is the only child in a group. Because of this, we also have to transfer heads up to the
-        // child, otherwise the invisible summary would be heads-upped.
+        // is the only child in a group. Because of this, we also have to transfer alert to the
+        // child, otherwise the invisible summary would be alerted.
         // This transfer to the child is not always correct in case the app has just posted another
         // child in addition to the existing one, but it hasn't arrived in systemUI yet. In such
-        // a scenario we would transfer the heads up to the old child and the wrong notification
-        // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the
+        // a scenario we would transfer the alert to the old child and the wrong notification
+        // would be alerted. In order to avoid this, we'll recover from this issue and alert the
         // summary again instead of the old child if it's within a certain timeout.
-        if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) {
+        if (SystemClock.elapsedRealtime() - group.lastAlertTransfer < ALERT_TRANSFER_TIMEOUT) {
             if (!onlySummaryAlerts(group.summary)) {
                 return;
             }
@@ -215,26 +240,24 @@
             int size = children.size();
             for (int i = 0; i < size; i++) {
                 NotificationData.Entry entry = children.get(i);
-                if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) {
+                if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
                     releasedChild = true;
-                    mHeadsUpManager.removeNotification(
-                            entry.key, true /* releaseImmediately */);
+                    alertManager.removeNotification(entry.key, true /* releaseImmediately */);
                 }
             }
             if (isolatedChild != null && onlySummaryAlerts(isolatedChild)
-                    && isolatedChild.row.isHeadsUp()) {
+                    && alertManager.isAlerting(isolatedChild.key)) {
                 releasedChild = true;
-                mHeadsUpManager.removeNotification(
-                        isolatedChild.key, true /* releaseImmediately */);
+                alertManager.removeNotification(isolatedChild.key, true /* releaseImmediately */);
             }
-            if (releasedChild && !mHeadsUpManager.contains(group.summary.key)) {
+            if (releasedChild && !alertManager.isAlerting(group.summary.key)) {
                 boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
                 if (notifyImmediately) {
-                    mHeadsUpManager.showNotification(group.summary);
+                    alertManager.showNotification(group.summary);
                 } else {
-                    group.hunSummaryOnNextAddition = true;
+                    group.alertSummaryOnNextAddition = true;
                 }
-                group.lastHeadsUpTransfer = 0;
+                group.lastAlertTransfer = 0;
             }
         }
     }
@@ -264,8 +287,8 @@
     }
 
     private void onEntryBecomingChild(NotificationData.Entry entry) {
-        if (entry.row.isHeadsUp()) {
-            onHeadsUpStateChanged(entry, true);
+        if (shouldIsolate(entry)) {
+            isolateNotification(entry);
         }
     }
 
@@ -281,7 +304,11 @@
                         && hasIsolatedChildren(group)));
         if (prevSuppressed != group.suppressed) {
             if (group.suppressed) {
-                handleSuppressedSummaryHeadsUpped(group.summary);
+                if (mHeadsUpManager.isAlerting(group.summary.key)) {
+                    handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
+                } else if (mAmbientPulseManager.isAlerting(group.summary.key)) {
+                    handleSuppressedSummaryAlerted(group.summary, mAmbientPulseManager);
+                }
             }
             if (!mIsUpdatingUnchangedGroup && mListener != null) {
                 mListener.onGroupsChanged();
@@ -495,54 +522,56 @@
     }
 
     @Override
+    public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+        onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
+    }
+
+    @Override
     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+        onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+    }
+
+    private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting,
+            AlertingNotificationManager alertManager) {
         final StatusBarNotification sbn = entry.notification;
-        if (entry.row.isHeadsUp()) {
-            if (shouldIsolate(sbn)) {
-                // We will be isolated now, so lets update the groups
-                onEntryRemovedInternal(entry, entry.notification);
-
-                mIsolatedEntries.put(sbn.getKey(), sbn);
-
-                onEntryAdded(entry);
-                // We also need to update the suppression of the old group, because this call comes
-                // even before the groupManager knows about the notification at all.
-                // When the notification gets added afterwards it is already isolated and therefore
-                // it doesn't lead to an update.
-                updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
-                mListener.onGroupsChanged();
-            } else {
-                handleSuppressedSummaryHeadsUpped(entry);
+        if (isAlerting) {
+            if (shouldIsolate(entry)) {
+                isolateNotification(entry);
+            } else if (sbn.getNotification().isGroupSummary()
+                    && isGroupSuppressed(sbn.getGroupKey())){
+                handleSuppressedSummaryAlerted(entry, alertManager);
             }
         } else {
-            if (mIsolatedEntries.containsKey(sbn.getKey())) {
-                // not isolated anymore, we need to update the groups
-                onEntryRemovedInternal(entry, entry.notification);
-                mIsolatedEntries.remove(sbn.getKey());
-                onEntryAdded(entry);
-                mListener.onGroupsChanged();
-            }
+            stopIsolatingNotification(entry);
         }
     }
 
-    private void handleSuppressedSummaryHeadsUpped(NotificationData.Entry entry) {
-        StatusBarNotification sbn = entry.notification;
+    /**
+     * Handles the scenario where a summary that has been suppressed is alerted.  A suppressed
+     * summary should for all intents and purposes be invisible to the user and as a result should
+     * not alert.  When this is the case, it is our responsibility to pass the alert to the
+     * appropriate child which will be the representative notification alerting for the group.
+     * @param summary the summary that is suppressed and alerting
+     * @param alertManager the alert manager that manages the alerting summary
+     */
+    private void handleSuppressedSummaryAlerted(@NonNull NotificationData.Entry summary,
+            @NonNull AlertingNotificationManager alertManager) {
+        StatusBarNotification sbn = summary.notification;
         if (!isGroupSuppressed(sbn.getGroupKey())
                 || !sbn.getNotification().isGroupSummary()
-                || !entry.row.isHeadsUp()) {
+                || !alertManager.isAlerting(sbn.getKey())) {
             return;
         }
 
-        // The parent of a suppressed group got huned, lets hun the child!
+        // The parent of a suppressed group got alerted, lets alert the child!
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
 
-        if (pendingInflationsWillAddChildren(notificationGroup)) {
-            // New children will actually be added to this group, let's not transfer the heads
-            // up
-            return;
-        }
-
         if (notificationGroup != null) {
+            if (pendingInflationsWillAddChildren(notificationGroup)) {
+                // New children will actually be added to this group, let's not transfer the alert.
+                return;
+            }
+
             Iterator<NotificationData.Entry> iterator
                     = notificationGroup.children.values().iterator();
             NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null;
@@ -551,20 +580,35 @@
             }
             if (child != null) {
                 if (child.row.keepInParent() || child.row.isRemoved() || child.row.isDismissed()) {
-                    // the notification is actually already removed, no need to do heads-up on it.
+                    // the notification is actually already removed, no need to do alert on it.
                     return;
                 }
-                if (mHeadsUpManager.contains(child.key)) {
-                    mHeadsUpManager.updateNotification(child.key, true /* alert */);
-                } else {
-                    if (onlySummaryAlerts(entry)) {
-                        notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime();
-                    }
-                    mHeadsUpManager.showNotification(child);
-                }
+                transferAlertStateToChild(summary, child, alertManager);
             }
         }
-        mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */);
+    }
+
+    /**
+     * Transfers the alert state from a given summary notification to the specified child.  The
+     * result is the child will now alert while the summary does not.
+     *
+     * @param summary the currently alerting summary notification
+     * @param child the child that should receive the alert
+     * @param alertManager the manager for the alert
+     */
+    private void transferAlertStateToChild(@NonNull NotificationData.Entry summary,
+            @NonNull NotificationData.Entry child,
+            @NonNull AlertingNotificationManager alertManager) {
+        NotificationGroup notificationGroup = mGroupMap.get(summary.notification.getGroupKey());
+        if (alertManager.isAlerting(child.key)) {
+            alertManager.updateNotification(child.key, true /* alert */);
+        } else {
+            if (onlySummaryAlerts(summary)) {
+                notificationGroup.lastAlertTransfer = SystemClock.elapsedRealtime();
+            }
+            alertManager.showNotification(child);
+        }
+        alertManager.removeNotification(summary.key, true /* releaseImmediately */);
     }
 
     private boolean onlySummaryAlerts(NotificationData.Entry entry) {
@@ -596,13 +640,69 @@
         return false;
     }
 
-    private boolean shouldIsolate(StatusBarNotification sbn) {
+    /**
+     * Whether a notification that is normally part of a group should be temporarily isolated from
+     * the group and put in their own group visually.  This generally happens when the notification
+     * is alerting.
+     *
+     * @param entry the notification to check
+     * @return true if the entry should be isolated
+     */
+
+    private boolean shouldIsolate(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
-        return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
-                && (sbn.getNotification().fullScreenIntent != null
-                        || notificationGroup == null
-                        || !notificationGroup.expanded
-                        || isGroupNotFullyVisible(notificationGroup));
+        if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
+            return false;
+        }
+        if (!mIsDozing && !mHeadsUpManager.isAlerting(entry.key)) {
+            return false;
+        }
+        if (mIsDozing && !mAmbientPulseManager.isAlerting(entry.key)) {
+            return false;
+        }
+        return (sbn.getNotification().fullScreenIntent != null
+                    || notificationGroup == null
+                    || !notificationGroup.expanded
+                    || isGroupNotFullyVisible(notificationGroup));
+    }
+
+    /**
+     * Isolate a notification from its group so that it visually shows as its own group.
+     *
+     * @param entry the notification to isolate
+     */
+    private void isolateNotification(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+
+        // We will be isolated now, so lets update the groups
+        onEntryRemovedInternal(entry, entry.notification);
+
+        mIsolatedEntries.put(sbn.getKey(), sbn);
+
+        onEntryAdded(entry);
+        // We also need to update the suppression of the old group, because this call comes
+        // even before the groupManager knows about the notification at all.
+        // When the notification gets added afterwards it is already isolated and therefore
+        // it doesn't lead to an update.
+        updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
+        mListener.onGroupsChanged();
+    }
+
+    /**
+     * Stop isolating a notification and re-group it with its original logical group.
+     *
+     * @param entry the notification to un-isolate
+     */
+    private void stopIsolatingNotification(NotificationData.Entry entry) {
+        StatusBarNotification sbn = entry.notification;
+        if (mIsolatedEntries.containsKey(sbn.getKey())) {
+            // not isolated anymore, we need to update the groups
+            onEntryRemovedInternal(entry, entry.notification);
+            mIsolatedEntries.remove(sbn.getKey());
+            onEntryAdded(entry);
+            mListener.onGroupsChanged();
+        }
     }
 
     private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
@@ -641,11 +741,11 @@
          */
         public boolean suppressed;
         /**
-         * The time when the last heads transfer from group to child happened, while the summary
-         * has the flags to heads up on its own.
+         * The time when the last alert transfer from group to child happened, while the summary
+         * has the flags to alert up on its own.
          */
-        public long lastHeadsUpTransfer;
-        public boolean hunSummaryOnNextAddition;
+        public long lastAlertTransfer;
+        public boolean alertSummaryOnNextAddition;
 
         @Override
         public String toString() {
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 459b4d7..e1d8638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -187,6 +187,7 @@
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.BackDropView;
@@ -254,7 +255,7 @@
         ActivityStarter, OnUnlockMethodChangedListener,
         OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
         ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter,
-        StatusBarStateController.StateListener {
+        StatusBarStateController.StateListener,  AmbientPulseManager.OnAmbientChangedListener {
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -632,7 +633,7 @@
 
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mColorExtractor.addOnColorsChangedListener(this);
-        mStatusBarStateController.addListener(this);
+        mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
 
@@ -865,6 +866,8 @@
         mHeadsUpManager.addListener(mNotificationPanel);
         mHeadsUpManager.addListener(mGroupManager);
         mHeadsUpManager.addListener(mVisualStabilityManager);
+        mAmbientPulseManager.addListener(this);
+        mAmbientPulseManager.addListener(mGroupManager);
         mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
@@ -1233,7 +1236,7 @@
     @Override
     public void onPerformRemoveNotification(StatusBarNotification n) {
         if (mNotificationPanel.hasPulsingNotifications() &&
-                    !mHeadsUpManager.hasNotifications()) {
+                    !mAmbientPulseManager.hasNotifications()) {
             // We were showing a pulse for a notification, but no notifications are pulsing anymore.
             // Finish the pulse.
             mDozeScrimController.pulseOutNow();
@@ -1697,8 +1700,12 @@
     }
 
     @Override
-    public boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
-        if (mIsOccluded && !isDozing()) {
+    public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+        if (isDozing()) {
+            return false;
+        }
+
+        if (mIsOccluded) {
             boolean devicePublic = mLockscreenUserManager.
                     isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
             boolean userPublic = devicePublic
@@ -1711,17 +1718,14 @@
 
         if (!panelsEnabled()) {
             if (DEBUG) {
-                Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+                Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
             }
             return false;
         }
 
         if (sbn.getNotification().fullScreenIntent != null) {
             if (mAccessibilityManager.isTouchExplorationEnabled()) {
-                if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
-                return false;
-            } else if (isDozing()) {
-                // We never want heads up when we are dozing.
+                if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
                 return false;
             } else {
                 // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
@@ -1797,9 +1801,16 @@
     @Override
     public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
         mEntryManager.updateNotificationRanking(null /* rankingMap */);
+    }
 
-        if (isHeadsUp) {
-            mDozeServiceHost.fireNotificationHeadsUp();
+    @Override
+    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+        mEntryManager.updateNotificationRanking(null);
+        if (isAmbient) {
+            mDozeServiceHost.fireNotificationPulse();
+        } else if (!mAmbientPulseManager.hasNotifications()) {
+            // There are no longer any notifications to show.  We should end the pulse now.
+            mDozeScrimController.pulseOutNow();
         }
     }
 
@@ -3453,7 +3464,14 @@
         mIsKeyguard = false;
         Trace.beginSection("StatusBar#hideKeyguard");
         boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
-        mStatusBarStateController.setState(StatusBarState.SHADE);
+        if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+            //TODO: StatusBarStateController should probably know about hiding the keyguard and
+            // notify listeners.
+
+            // If the state didn't change, we may still need to update public mode
+            updatePublicMode();
+            mEntryManager.updateNotifications();
+        }
         View viewToClick = null;
         if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
             if (!mKeyguardRequested) {
@@ -3596,6 +3614,7 @@
         mKeyguardIndicationController.setDozing(mDozing);
         mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
         mNotificationLogger.setDozing(mDozing);
+        mGroupManager.setDozing(mDozing);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -4142,6 +4161,7 @@
         @Override
         public void onStartedWakingUp() {
             mDeviceInteractive = true;
+            mAmbientPulseManager.releaseAllImmediately();
             mVisualStabilityManager.setScreenOn(true);
             mNotificationPanel.setTouchAndAnimationDisabled(false);
             mDozeServiceHost.stopDozing();
@@ -4174,11 +4194,7 @@
         public void onScreenTurnedOff() {
             mFalsingManager.onScreenOff();
             mScrimController.onScreenTurnedOff();
-            // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
-            // in that case destroys the HeadsUpManager state, so don't do it in that case.
-            if (!isPulsing()) {
-                updateIsKeyguard();
-            }
+            updateIsKeyguard();
         }
     };
 
@@ -4417,9 +4433,9 @@
             }
         }
 
-        public void fireNotificationHeadsUp() {
+        public void fireNotificationPulse() {
             for (Callback callback : mCallbacks) {
-                callback.onNotificationHeadsUp();
+                callback.onNotificationAlerted();
             }
         }
 
@@ -4455,7 +4471,7 @@
                 @Override
                 public void onPulseStarted() {
                     callback.onPulseStarted();
-                    if (mHeadsUpManager.hasNotifications()) {
+                    if (mAmbientPulseManager.hasNotifications()) {
                         // Only pulse the stack scroller if there's actually something to show.
                         // Otherwise just show the always-on screen.
                         setPulsing(true);
@@ -4536,7 +4552,11 @@
 
         @Override
         public void extendPulse() {
-            mDozeScrimController.extendPulse();
+            if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) {
+                mAmbientPulseManager.extendPulse();
+            } else {
+                mDozeScrimController.extendPulse();
+            }
         }
 
         @Override
@@ -4622,6 +4642,8 @@
     // for heads up notifications
     protected HeadsUpManagerPhone mHeadsUpManager;
 
+    protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+
     private AboveShelfObserver mAboveShelfObserver;
 
     // handling reordering
@@ -4727,7 +4749,7 @@
         final boolean wasOccluded = mIsOccluded;
         dismissKeyguardThenExecute(() -> {
             // TODO: Some of this code may be able to move to NotificationEntryManager.
-            if (mHeadsUpManager != null && mHeadsUpManager.contains(notificationKey)) {
+            if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
                 // Release the HUN notification to the shade.
 
                 if (isPresenterFullyCollapsed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e8389af..3db1456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -460,6 +460,11 @@
                 boolean staying = mStatusBar.hideKeyguard();
                 if (!staying) {
                     mStatusBarWindowController.setKeyguardFadingAway(true);
+                    // hide() will happen asynchronously and might arrive after the scrims
+                    // were already hidden, this means that the transition callback won't
+                    // be triggered anymore and StatusBarWindowController will be forever in
+                    // the fadingAway state.
+                    mStatusBar.updateScrimController();
                     wakeAndUnlockDejank();
                 } else {
                     mStatusBar.finishKeyguardFadingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 51a5df2..57c7e28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -93,7 +93,8 @@
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
         mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addListener(
+                mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 52b813f..7dd0d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -586,7 +586,8 @@
             final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
 
             if (mShowImeOnInputConnection && inputConnection != null) {
-                final InputMethodManager imm = InputMethodManager.getInstance();
+                final InputMethodManager imm =
+                        getContext().getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     // onCreateInputConnection is called by InputMethodManager in the middle of
                     // setting up the connection to the IME; wait with requesting the IME until that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 0adb439..29a6b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -403,7 +403,7 @@
                 boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
                 return new Pair<Integer, Boolean>(userId[0], hasCACerts);
             } catch (RemoteException | InterruptedException | AssertionError e) {
-                Log.i(TAG, e.getMessage());
+                Log.i(TAG, "failed to get CA certs", e);
                 new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(
                         () -> new CACertLoader().execute(userId[0]),
                         CA_CERT_LOADING_RETRY_TIME_IN_MS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index c2da7f5..5c8336c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -88,14 +88,14 @@
         mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
         mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
 
-        mHost.callback.onNotificationHeadsUp();
+        mHost.callback.onNotificationAlerted();
 
         mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
 
         verify(mMachine, never()).requestState(any());
         verify(mMachine, never()).requestPulse(anyInt());
 
-        mHost.callback.onNotificationHeadsUp();
+        mHost.callback.onNotificationAlerted();
 
         mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f21ce27..8b41516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -56,19 +56,19 @@
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
 
-    private static final int TEST_MINIMUM_DISPLAY_TIME = 200;
-    private static final int TEST_AUTO_DISMISS_TIME = 500;
+    protected static final int TEST_MINIMUM_DISPLAY_TIME = 200;
+    protected static final int TEST_AUTO_DISMISS_TIME = 500;
     // Number of notifications to use in tests requiring multiple notifications
     private static final int TEST_NUM_NOTIFICATIONS = 4;
-    private static final int TEST_TIMEOUT_TIME = 10000;
-    private final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
+    protected static final int TEST_TIMEOUT_TIME = 10000;
+    protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
 
     private AlertingNotificationManager mAlertingNotificationManager;
 
     protected NotificationData.Entry mEntry;
     protected Handler mTestHandler;
     private StatusBarNotification mSbn;
-    private boolean mTimedOut = false;
+    protected boolean mTimedOut = false;
 
     @Mock protected ExpandableNotificationRow mRow;
 
@@ -122,7 +122,7 @@
     public void testShowNotification_addsEntry() {
         mAlertingNotificationManager.showNotification(mEntry);
 
-        assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+        assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
         assertTrue(mAlertingNotificationManager.hasNotifications());
         assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.key));
     }
@@ -136,7 +136,7 @@
         TestableLooper.get(this).processMessages(1);
 
         assertFalse("Test timed out", mTimedOut);
-        assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+        assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
     }
 
     @Test
@@ -146,7 +146,7 @@
         // Try to remove but defer, since the notification has not been shown long enough.
         mAlertingNotificationManager.removeNotification(mEntry.key, false /* releaseImmediately */);
 
-        assertTrue(mAlertingNotificationManager.contains(mEntry.key));
+        assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
     }
 
     @Test
@@ -156,7 +156,7 @@
         // Remove forcibly with releaseImmediately = true.
         mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */);
 
-        assertFalse(mAlertingNotificationManager.contains(mEntry.key));
+        assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
     }
 
     @Test
@@ -174,10 +174,18 @@
     }
 
     @Test
-    public void testShouldExtendLifetime_notShownLongEnough() {
+    public void testCanRemoveImmediately_notShownLongEnough() {
         mAlertingNotificationManager.showNotification(mEntry);
 
-        // The entry has just been added so the lifetime should be extended
+        // The entry has just been added so we should not remove immediately.
+        assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.key));
+    }
+
+    @Test
+    public void testShouldExtendLifetime() {
+        mAlertingNotificationManager.showNotification(mEntry);
+
+        // While the entry is alerting, it should not be removable.
         assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
new file mode 100644
index 0000000..f0344e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AmbientPulseManagerTest extends AlertingNotificationManagerTest {
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    private static final int TEST_EXTENSION_TIME = 500;
+    private AmbientPulseManager mAmbientPulseManager;
+    private boolean mLivesPastNormalTime;
+
+    protected AlertingNotificationManager createAlertingNotificationManager() {
+        return mAmbientPulseManager;
+    }
+
+    @Before
+    public void setUp() {
+        mAmbientPulseManager = new AmbientPulseManager(mContext);
+        mAmbientPulseManager.mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
+        mAmbientPulseManager.mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+        mAmbientPulseManager.mExtensionTime = TEST_EXTENSION_TIME;
+        super.setUp();
+        mAmbientPulseManager.mHandler = mTestHandler;
+    }
+
+    @Test
+    public void testExtendPulse() {
+        mAmbientPulseManager.showNotification(mEntry);
+        Runnable pastNormalTimeRunnable =
+                () -> mLivesPastNormalTime = mAmbientPulseManager.isAlerting(mEntry.key);
+        mTestHandler.postDelayed(pastNormalTimeRunnable,
+                mAmbientPulseManager.mAutoDismissNotificationDecay +
+                        mAmbientPulseManager.mExtensionTime / 2);
+        mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
+
+        mAmbientPulseManager.extendPulse();
+
+        // Wait for normal time runnable and extended remove runnable and process them on arrival.
+        TestableLooper.get(this).processMessages(2);
+
+        assertFalse("Test timed out", mTimedOut);
+        assertTrue("Pulse was not extended", mLivesPastNormalTime);
+        assertFalse(mAmbientPulseManager.isAlerting(mEntry.key));
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index b2170fa..edf29ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,6 +31,7 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -63,6 +64,7 @@
         mContext = context;
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+        mGroupManager.setHeadsUpManager(mHeadsUpManager);
     }
 
     public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 087aa59..2728453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.NotificationHeaderView;
@@ -51,7 +50,7 @@
 
     @Test
     public void testGetMaxAllowedVisibleChildren_ambient() {
-        mGroup.setShowAmbient(true);
+        mGroup.setOnAmbient(true);
         Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
             NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index a81d17f..1070795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -82,28 +82,26 @@
         // Remove should succeed because the notification is swiped out
         mHeadsUpManager.removeNotification(mEntry.key, false /* releaseImmediately */);
 
-        assertFalse(mHeadsUpManager.contains(mEntry.key));
+        assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
     }
 
     @Test
-    public void testShouldExtendLifetime_swipedOut() {
+    public void testCanRemoveImmediately_swipedOut() {
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.addSwipedOutNotification(mEntry.key);
 
-        // Notification is swiped so its lifetime should not be extended even if it hasn't been
-        // shown long enough
-        assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+        // Notification is swiped so it can be immediately removed.
+        assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
     }
 
     @Test
-    public void testShouldExtendLifetime_notTopEntry() {
+    public void testCanRemoveImmediately_notTopEntry() {
         NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
         laterEntry.row = mRow;
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
 
-        // Notification is "behind" a higher priority notification so we have no reason to keep
-        // its lifetime extended
-        assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+        // Notification is "behind" a higher priority notification so we can remove it immediately.
+        assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 6a3c8a8..464f74b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -36,6 +36,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -60,15 +61,23 @@
     private static final String TEST_CHANNEL_ID = "test_channel";
     private static final String TEST_GROUP_ID = "test_group";
     private static final String TEST_PACKAGE_NAME = "test_pkg";
-    private NotificationGroupManager mGroupManager = new NotificationGroupManager();
+    private NotificationGroupManager mGroupManager;
     private int mId = 0;
 
     @Mock HeadsUpManager mHeadsUpManager;
+    @Mock AmbientPulseManager mAmbientPulseManager;
 
     @Before
     public void setup() {
-         mGroupManager.setHeadsUpManager(mHeadsUpManager);
-         mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
+        mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+
+        initializeGroupManager();
+    }
+
+    private void initializeGroupManager() {
+        mGroupManager = new NotificationGroupManager();
+        mGroupManager.setHeadsUpManager(mHeadsUpManager);
+        mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
     }
 
     @Test
@@ -141,8 +150,7 @@
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(createChildNotification());
-        when(childEntry.row.isHeadsUp()).thenReturn(true);
-        when(mHeadsUpManager.contains(childEntry.key)).thenReturn(true);
+        when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true);
 
         mGroupManager.onHeadsUpStateChanged(childEntry, true);
 
@@ -154,17 +162,35 @@
     }
 
     @Test
+    public void testAmbientPulseEntryIsIsolated() {
+        mGroupManager.setDozing(true);
+        NotificationData.Entry childEntry = createChildNotification();
+        NotificationData.Entry summaryEntry = createSummaryNotification();
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(childEntry);
+        mGroupManager.onEntryAdded(createChildNotification());
+        when(mAmbientPulseManager.isAlerting(childEntry.key)).thenReturn(true);
+
+        mGroupManager.onAmbientStateChanged(childEntry, true);
+
+        // Child entries that are heads upped should be considered separate groups visually even if
+        // they are the same group logically
+        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry.row,
+                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+    }
+
+    @Test
     public void testSuppressedSummaryHeadsUpTransfersToChild() {
         NotificationData.Entry summaryEntry = createSummaryNotification();
-        when(summaryEntry.row.isHeadsUp()).thenReturn(true);
-        when(mHeadsUpManager.contains(summaryEntry.key)).thenReturn(true);
+        when(mHeadsUpManager.isAlerting(summaryEntry.key)).thenReturn(true);
         NotificationData.Entry childEntry = createChildNotification();
 
-        // Summary will be suppressed because there is only one child
+        // Summary will be suppressed because there is only one child.
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        // A suppressed summary should transfer its heads up state to the child
+        // A suppressed summary should transfer its heads up state to the child.
         verify(mHeadsUpManager, never()).showNotification(summaryEntry);
         verify(mHeadsUpManager).showNotification(childEntry);
     }
@@ -175,24 +201,64 @@
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
         NotificationData.Entry summaryEntry =
                 createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(summaryEntry.row.isHeadsUp()).thenReturn(true);
         NotificationData.Entry childEntry =
                 createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry2 =
                 createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+        mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of heads up state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
-        when(summaryEntry.row.isHeadsUp()).thenReturn(false);
-        when(childEntry.row.isHeadsUp()).thenReturn(true);
 
         // Add second child notification so that summary is no longer suppressed.
         mGroupManager.onEntryAdded(childEntry2);
 
         // The heads up state should transfer back to the summary as there is now more than one
         // child and the summary should no longer be suppressed.
-        assertTrue(mHeadsUpManager.contains(summaryEntry.key));
-        assertFalse(mHeadsUpManager.contains(childEntry.key));
+        assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+    }
+
+    @Test
+    public void testSuppressedSummaryAmbientPulseTransfersToChild() {
+        mGroupManager.setDozing(true);
+        NotificationData.Entry summaryEntry = createSummaryNotification();
+        when(mAmbientPulseManager.isAlerting(summaryEntry.key)).thenReturn(true);
+        NotificationData.Entry childEntry = createChildNotification();
+
+        // Summary will be suppressed because there is only one child.
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(childEntry);
+
+        // A suppressed summary should transfer its ambient state to the child.
+        verify(mAmbientPulseManager, never()).showNotification(summaryEntry);
+        verify(mAmbientPulseManager).showNotification(childEntry);
+    }
+
+    @Test
+    public void testSuppressedSummaryAmbientPulseTransfersToChildButBackAgain() {
+        mGroupManager.setDozing(true);
+        mAmbientPulseManager = new AmbientPulseManager(mContext);
+        mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+        initializeGroupManager();
+        NotificationData.Entry summaryEntry =
+                createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+        NotificationData.Entry childEntry =
+                createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+        NotificationData.Entry childEntry2 =
+                createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+        mAmbientPulseManager.showNotification(summaryEntry);
+        // Trigger a transfer of ambient state from summary to child.
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(childEntry);
+
+        // Add second child notification so that summary is no longer suppressed.
+        mGroupManager.onEntryAdded(childEntry2);
+
+        // The ambient state should transfer back to the summary as there is now more than one
+        // child and the summary should no longer be suppressed.
+        assertTrue(mAmbientPulseManager.isAlerting(summaryEntry.key));
+        assertFalse(mAmbientPulseManager.isAlerting(childEntry.key));
     }
 
     private NotificationData.Entry createSummaryNotification() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cbba251..5006b0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -358,7 +358,7 @@
     }
 
     @Test
-    public void testShouldPeek_nonSuppressedGroupSummary() {
+    public void testShouldHeadsUp_nonSuppressedGroupSummary() {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
@@ -375,11 +375,11 @@
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
 
-        assertTrue(mEntryManager.shouldPeek(entry, sbn));
+        assertTrue(mEntryManager.shouldHeadsUp(entry));
     }
 
     @Test
-    public void testShouldPeek_suppressedGroupSummary() {
+    public void testShouldHeadsUp_suppressedGroupSummary() {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false);
@@ -396,11 +396,11 @@
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
 
-        assertFalse(mEntryManager.shouldPeek(entry, sbn));
+        assertFalse(mEntryManager.shouldHeadsUp(entry));
     }
 
     @Test
-    public void testShouldPeek_suppressedPeek() {
+    public void testShouldHeadsUp_suppressedHeadsUp() {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
@@ -414,11 +414,11 @@
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
 
-        assertFalse(mEntryManager.shouldPeek(entry, sbn));
+        assertFalse(mEntryManager.shouldHeadsUp(entry));
     }
 
     @Test
-    public void testShouldPeek_noSuppressedPeek() {
+    public void testShouldHeadsUp_noSuppressedHeadsUp() {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
         when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
@@ -432,31 +432,31 @@
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
 
-        assertTrue(mEntryManager.shouldPeek(entry, sbn));
+        assertTrue(mEntryManager.shouldHeadsUp(entry));
     }
 
     @Test
-    public void testPeek_disabledStatusBar() {
+    public void testHeadsUp_disabledStatusBar() {
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
 
-        assertFalse("The panel shouldn't allow peek while disabled",
-                mStatusBar.shouldPeek(entry, sbn));
+        assertFalse("The panel shouldn't allow heads up while disabled",
+                mStatusBar.canHeadsUp(entry, sbn));
     }
 
     @Test
-    public void testPeek_disabledNotificationShade() {
+    public void testHeadsUp_disabledNotificationShade() {
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
 
-        assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
-                mStatusBar.shouldPeek(entry, sbn));
+        assertFalse("The panel shouldn't allow heads up while notification shade disabled",
+                mStatusBar.canHeadsUp(entry, sbn));
     }
 
     @Test
@@ -472,7 +472,7 @@
     }
 
     @Test
-    public void testPanelOpenForPeek() {
+    public void testPanelOpenForHeadsUp() {
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
         when(mNotificationList.size()).thenReturn(5);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b71d7a7..da52d40 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1834,7 +1834,7 @@
 
                 if (provider.widgets.isEmpty()) {
                     // cancel the future updates
-                    cancelBroadcasts(provider);
+                    cancelBroadcastsLocked(provider);
 
                     // send the broacast saying that the provider is not in use any more
                     sendDisabledIntentLocked(provider);
@@ -1843,18 +1843,16 @@
         }
     }
 
-    private void cancelBroadcasts(Provider provider) {
+    private void cancelBroadcastsLocked(Provider provider) {
         if (DEBUG) {
-            Slog.i(TAG, "cancelBroadcasts() for " + provider);
+            Slog.i(TAG, "cancelBroadcastsLocked() for " + provider);
         }
         if (provider.broadcast != null) {
-            mAlarmManager.cancel(provider.broadcast);
-            long token = Binder.clearCallingIdentity();
-            try {
-                provider.broadcast.cancel();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            final PendingIntent broadcast = provider.broadcast;
+            mSaveStateHandler.post(() -> {
+                    mAlarmManager.cancel(broadcast);
+                    broadcast.cancel();
+            });
             provider.broadcast = null;
         }
     }
@@ -2315,7 +2313,7 @@
         mProviders.remove(provider);
 
         // no need to send the DISABLE broadcast, since the receiver is gone anyway
-        cancelBroadcasts(provider);
+        cancelBroadcastsLocked(provider);
     }
 
     private void sendEnableIntentLocked(Provider p) {
@@ -2369,17 +2367,14 @@
                 Binder.restoreCallingIdentity(token);
             }
             if (!alreadyRegistered) {
-                long period = provider.info.updatePeriodMillis;
-                if (period < MIN_UPDATE_PERIOD) {
-                    period = MIN_UPDATE_PERIOD;
-                }
-                final long oldId = Binder.clearCallingIdentity();
-                try {
+                // Set the alarm outside of our locks; we've latched the first-time
+                // invariant and established the PendingIntent safely.
+                final long period = Math.max(provider.info.updatePeriodMillis, MIN_UPDATE_PERIOD);
+                final PendingIntent broadcast = provider.broadcast;
+                mSaveStateHandler.post(() ->
                     mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            SystemClock.elapsedRealtime() + period, period, provider.broadcast);
-                } finally {
-                    Binder.restoreCallingIdentity(oldId);
-                }
+                            SystemClock.elapsedRealtime() + period, period, broadcast)
+                );
             }
         }
     }
@@ -3382,7 +3377,7 @@
                             // Reschedule for the new updatePeriodMillis (don't worry about handling
                             // it specially if updatePeriodMillis didn't change because we just sent
                             // an update, and the next one will be updatePeriodMillis from now).
-                            cancelBroadcasts(provider);
+                            cancelBroadcastsLocked(provider);
                             registerForBroadcastsLocked(provider, appWidgetIds);
                             // If it's currently showing, call back with the new
                             // AppWidgetProviderInfo.
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index ad80cc26..2671327 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -587,6 +587,7 @@
             }
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
+                if (sVerbose) Slog.v(LOG_TAG, "calling onFillRequest() for id=" + mRequest.getId());
                 try {
                     remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
@@ -659,6 +660,7 @@
         public void run() {
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
+                if (sVerbose) Slog.v(LOG_TAG, "calling onSaveRequest()");
                 try {
                     remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 9af952d..c933833 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -105,7 +105,7 @@
                 try {
                     IBackupTransport transport =
                             transportClient.connectOrThrow(
-                                    "AppBackupUtils.appIsEligibleForBackupAtRuntime");
+                                    "AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport");
                     return transport.isAppEligibleForBackup(
                             packageInfo, AppBackupUtils.appGetsFullBackup(packageInfo));
                 } catch (Exception e) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 380f6a7..a69d416 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1490,23 +1490,19 @@
         }
     }
 
-    private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+    private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
 
     private void enforceTunnelPermissions(String callingPackage) {
         checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
-        if (false) { // STOPSHIP if this line is present
-            switch (getAppOpsManager().noteOp(
-                        TUNNEL_OP,
-                        Binder.getCallingUid(), callingPackage)) {
-                case AppOpsManager.MODE_DEFAULT:
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
-                    break;
-                case AppOpsManager.MODE_ALLOWED:
-                    return;
-                default:
-                    throw new SecurityException("Request to ignore AppOps for non-legacy API");
-            }
+        switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+            case AppOpsManager.MODE_DEFAULT:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+                break;
+            case AppOpsManager.MODE_ALLOWED:
+                return;
+            default:
+                throw new SecurityException("Request to ignore AppOps for non-legacy API");
         }
     }
 
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index ee01d86..4f0e170 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -93,10 +93,12 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         List<LooperStats.ExportedEntry> entries = mStats.getEntries();
         entries.sort(Comparator
-                .comparing((LooperStats.ExportedEntry entry) -> entry.threadName)
+                .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
+                .thenComparing(entry -> entry.threadName)
                 .thenComparing(entry -> entry.handlerClassName)
                 .thenComparing(entry -> entry.messageName));
         String header = String.join(",", Arrays.asList(
+                "work_source_uid",
                 "thread_name",
                 "handler_class",
                 "message_name",
@@ -110,11 +112,11 @@
                 "exception_count"));
         pw.println(header);
         for (LooperStats.ExportedEntry entry : entries) {
-            pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName,
-                    entry.handlerClassName, entry.messageName, entry.isInteractive,
-                    entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros,
-                    entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros,
-                    entry.exceptionCount);
+            pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.workSourceUid,
+                    entry.threadName, entry.handlerClassName, entry.messageName,
+                    entry.isInteractive, entry.messageCount, entry.recordedMessageCount,
+                    entry.totalLatencyMicros, entry.maxLatencyMicros, entry.cpuUsageMicros,
+                    entry.maxCpuUsageMicros, entry.exceptionCount);
         }
     }
 
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index cb03255..f436286 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -42,31 +42,25 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.Sandman;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
 import android.util.Slog;
 
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-
 import com.android.internal.R;
 import com.android.internal.app.DisableCarModeActivity;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.DumpUtils;
-import com.android.server.power.ShutdownThread;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -466,7 +460,7 @@
             uiMode |= mNightMode << 4;
         }
 
-        if (mPowerSave && !mNightModeLocked) {
+        if (mPowerSave) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7533db1..b898a90 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8406,10 +8406,17 @@
         }
     }
 
+    /** @deprecated - Use {@link #removeContentProviderExternalAsUser} which takes a user ID. */
+    @Deprecated
+    @Override
     public void removeContentProviderExternal(String name, IBinder token) {
+        removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
         enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
             "Do not have permission in call removeContentProviderExternal()");
-        int userId = UserHandle.getCallingUserId();
         long ident = Binder.clearCallingIdentity();
         try {
             removeContentProviderExternalUnchecked(name, token, userId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 628207c..2199bb7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,18 +17,18 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
 import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
 import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
 import static android.app.ActivityOptions.ANIM_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -81,6 +81,8 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
@@ -93,6 +95,13 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -110,14 +119,6 @@
 import static com.android.server.am.TaskPersister.DEBUG;
 import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -132,6 +133,7 @@
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.ResultInfo;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ClientTransaction;
@@ -143,7 +145,6 @@
 import android.app.servertransaction.PipModeChangeItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.WindowVisibilityItem;
-import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -1279,20 +1280,14 @@
 
     /**
      * Check whether this activity can be launched on the specified display.
+     *
      * @param displayId Target display id.
-     * @return {@code true} if either it is the default display or this activity is resizeable and
-     *         can be put a secondary screen.
+     * @return {@code true} if either it is the default display or this activity can be put on a
+     *         secondary screen.
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
-        final TaskRecord task = getTask();
-
-        // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
-        // This allows for a non resizable activity to be launched into a resizeable task.
-        final boolean resizeable =
-                task != null ? task.isResizeable() : supportsResizeableMultiWindow();
-
-        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
-                resizeable, launchedFromPid, launchedFromUid, info);
+        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+                launchedFromUid, info);
     }
 
     /**
@@ -2495,6 +2490,14 @@
         }
     }
 
+    /**
+     * @return {@code true} if this activity was reparented to another display but
+     *         {@link #ensureActivityConfiguration} is not called.
+     */
+    boolean shouldUpdateConfigForDisplayChanged() {
+        return mLastReportedDisplayId != getDisplayId();
+    }
+
     boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
         return ensureActivityConfiguration(globalChanges, preserveWindow,
                 false /* ignoreStopState */);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aa4e68d..91e677b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5375,12 +5375,14 @@
         display.positionChildAtTop(this, false /* includingParents */);
     }
 
+    /** NOTE: Should only be called from {@link TaskRecord#reparent}. */
     void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
             boolean setPause, String reason) {
         if (!moveToFront) {
             return;
         }
 
+        final ActivityState origState = r.getState();
         // If the activity owns the last resumed activity, transfer that together,
         // so that we don't resume the same activity again in the new stack.
         // Apps may depend on onResume()/onPause() being called in pairs.
@@ -5393,9 +5395,14 @@
             mPausingActivity = r;
             schedulePauseTimeout(r);
         }
-        // Move the stack in which we are placing the activity to the front. The call will also
-        // make sure the activity focus is set.
+        // Move the stack in which we are placing the activity to the front.
         moveToFront(reason);
+        // If the original state is resumed, there is no state change to update focused app.
+        // So here makes sure the activity focus is set if it is the top.
+        if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+            // TODO(b/111361570): Support multiple focused apps in WM
+            mService.setResumedActivityUncheckLocked(r, reason);
+        }
     }
 
     public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 310898e..d096fed 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -488,8 +488,8 @@
     }
 
     /** Check if placing task or activity on specified display is allowed. */
-    boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
-            int callingUid, ActivityInfo activityInfo) {
+    boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
+            ActivityInfo activityInfo) {
         if (displayId == DEFAULT_DISPLAY) {
             // No restrictions for the default display.
             return true;
@@ -498,10 +498,6 @@
             // Can't launch on secondary displays if feature is not supported.
             return false;
         }
-        if (!resizeable && !displayConfigMatchesGlobal(displayId)) {
-            // Can't apply wrong configuration to non-resizeable activities.
-            return false;
-        }
         if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
             // Can't place activities to a display that has restricted launch rules.
             // In this case the request should be made by explicitly adding target display id and
@@ -690,7 +686,7 @@
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
         final Display[] displays = mDisplayManager.getDisplays();
-        for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
+        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
             final Display display = displays[displayNdx];
             final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
             if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
@@ -4530,11 +4526,14 @@
             mService.setTaskWindowingMode(task.taskId,
                     WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
             if (preferredDisplayId != actualDisplayId) {
-                // Display a warning toast that we tried to put a non-resizeable task on a secondary
-                // display with config different from global config.
+                Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId);
+                // Display a warning toast that we failed to put a task on a secondary display.
                 mService.getTaskChangeNotificationController()
                         .notifyActivityLaunchOnSecondaryDisplayFailed();
                 return;
+            } else if (!forceNonResizable && handleForcedResizableTask(task,
+                    FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) {
+                return;
             }
         }
 
@@ -4554,16 +4553,23 @@
             return;
         }
 
+        handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN);
+    }
+
+    /**
+     * @return {@code true} if the top activity of the task is forced to be resizable and the user
+     *         was notified about activity being forced resized.
+     */
+    private boolean handleForcedResizableTask(TaskRecord task, int reason) {
         final ActivityRecord topActivity = task.getTopActivity();
         if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
-            && !topActivity.noDisplay) {
+                && !topActivity.noDisplay) {
             final String packageName = topActivity.appInfo.packageName;
-            final int reason = isSecondaryDisplayPreferred
-                    ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
-                    : FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
             mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
                     task.taskId, reason, packageName);
+            return true;
         }
+        return false;
     }
 
     void activityRelaunchedLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bcb2b9b..7df255d 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -945,7 +945,8 @@
                 auxiliaryResponse == null ? null : auxiliaryResponse.filters);
     }
 
-    void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
+    void postStartActivityProcessing(ActivityRecord r, int result,
+            ActivityStack startedActivityStack) {
         if (ActivityManager.isStartResultFatalError(result)) {
             return;
         }
@@ -957,14 +958,6 @@
         // about this, so it waits for the new activity to become visible instead.
         mSupervisor.reportWaitingActivityLaunchedIfNeeded(r, result);
 
-        ActivityStack startedActivityStack = null;
-        final ActivityStack currentStack = r.getStack();
-        if (currentStack != null) {
-            startedActivityStack = currentStack;
-        } else if (mTargetStack != null) {
-            startedActivityStack = targetStack;
-        }
-
         if (startedActivityStack == null) {
             return;
         }
@@ -1240,23 +1233,41 @@
                 int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                 ActivityRecord[] outActivity) {
         int result = START_CANCELED;
+        final ActivityStack startedActivityStack;
         try {
             mService.mWindowManager.deferSurfaceLayout();
             result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                     startFlags, doResume, options, inTask, outActivity);
         } finally {
-            // If we are not able to proceed, disassociate the activity from the task. Leaving an
-            // activity in an incomplete state can lead to issues, such as performing operations
-            // without a window container.
-            final ActivityStack stack = mStartActivity.getStack();
-            if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
-                stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
-                        null /* intentResultData */, "startActivity", true /* oomAdj */);
+            final ActivityStack currentStack = r.getStack();
+            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+            if (ActivityManager.isStartResultSuccessful(result)) {
+                if (startedActivityStack != null) {
+                    // If there is no state change (e.g. a resumed activity is reparented to
+                    // top of another display) to trigger a visibility/configuration checking,
+                    // we have to update the configuration for changing to different display.
+                    final ActivityRecord currentTop =
+                            startedActivityStack.topRunningActivityLocked();
+                    if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+                        mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+                                true /* markFrozenIfConfigChanged */, false /* deferResume */);
+                    }
+                }
+            } else {
+                // If we are not able to proceed, disassociate the activity from the task.
+                // Leaving an activity in an incomplete state can lead to issues, such as
+                // performing operations without a window container.
+                final ActivityStack stack = mStartActivity.getStack();
+                if (stack != null) {
+                    stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+                            null /* intentResultData */, "startActivity", true /* oomAdj */);
+                }
             }
             mService.mWindowManager.continueSurfaceLayout();
         }
 
-        postStartActivityProcessing(r, result, mTargetStack);
+        postStartActivityProcessing(r, result, startedActivityStack);
 
         return result;
     }
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 212844a..6935703d 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -4688,11 +4688,7 @@
         return mSleeping;
     }
 
-    /**
-     * Update AMS states when an activity is resumed. This should only be called by
-     * {@link ActivityStack#onActivityStateChanged(
-     * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
-     */
+    /** Update AMS states when an activity is resumed. */
     void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
         final TaskRecord task = r.getTask();
         if (task.isActivityTypeStandard()) {
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index f9dccea0..aad890b 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -22,6 +22,7 @@
 
 import android.annotation.Nullable;
 import android.os.FileUtils;
+import android.os.SystemProperties;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +39,10 @@
 final class MemoryStatUtil {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
 
+    /** True if device has per-app memcg */
+    private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
+            SystemProperties.getBoolean("ro.config.per_app_memcg", false);
+
     /** Path to check if device has memcg */
     private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
     /** Path to memory stat file for logging app start memory state */
@@ -55,15 +60,12 @@
     private static final int PGMAJFAULT_INDEX = 11;
     private static final int RSS_IN_BYTES_INDEX = 23;
 
-    /** True if device has memcg */
-    private static volatile Boolean sDeviceHasMemCg;
-
     private MemoryStatUtil() {}
 
     /**
      * Reads memory stat for a process.
      *
-     * Reads from memcg if available on device, else fallback to procfs.
+     * Reads from per-app memcg if available on device, else fallback to procfs.
      * Returns null if no stats can be read.
      */
     @Nullable
@@ -156,15 +158,10 @@
     }
 
     /**
-     * Checks if memcg is available on device.
-     *
-     * Touches the filesystem to do the check.
+     * Returns whether per-app memcg is available on device.
      */
     static boolean hasMemcg() {
-        if (sDeviceHasMemCg == null) {
-            sDeviceHasMemCg = (new File(MEMCG_TEST_PATH)).exists();
-        }
-        return sDeviceHasMemCg;
+        return DEVICE_HAS_PER_APP_MEMCG;
     }
 
     static final class MemoryStat {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7256e23..9b42d65 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1521,14 +1521,14 @@
 
     /**
      * Check whether this task can be launched on the specified display.
+     *
      * @param displayId Target display id.
-     * @return {@code true} if either it is the default display or this activity is resizeable and
-     *         can be put a secondary screen.
+     * @return {@code true} if either it is the default display or this activity can be put on a
+     *         secondary display.
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
         return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
-                isResizeable(false /* checkSupportsPip */), -1 /* don't check PID */,
-                -1 /* don't check UID */, null /* activityInfo */);
+                -1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index c028a43..873a8e3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -639,12 +639,20 @@
             }
         }
 
-        // Otherwise only focused applications can access the clipboard.
-        boolean uidFocused = mWm.isUidFocused(callingUid);
-        if (!uidFocused) {
-            Slog.e(TAG, "Denying clipboard access to " + callingPackage
-                    + ", application is not in focus.");
+        switch (op) {
+            case AppOpsManager.OP_READ_CLIPBOARD:
+                // Clipboard can only be read by applications with focus.
+                boolean uidFocused = mWm.isUidFocused(callingUid);
+                if (!uidFocused) {
+                    Slog.e(TAG, "Denying clipboard access to " + callingPackage
+                            + ", application is not in focus.");
+                }
+                return uidFocused;
+            case AppOpsManager.OP_WRITE_CLIPBOARD:
+                // Writing is allowed without focus.
+                return true;
+            default:
+                throw new IllegalArgumentException("Unknown clipboard appop " + op);
         }
-        return uidFocused;
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b5a9f74..3264790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,45 +18,11 @@
 
 import android.content.ComponentName;
 
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-
 /**
  * Input method manager local system service interface.
  */
 public abstract class InputMethodManagerInternal {
     /**
-     * Called by the window manager service when a client process is being attached to the window
-     * manager service.
-     *
-     * <p>The caller must not have WindowManagerService lock.  This method internally acquires
-     * InputMethodManagerService lock.</p>
-     *
-     * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
-     *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
-     *               process
-     * @param inputContext communication channel for the dummy
-     *                     {@link android.view.inputmethod.InputConnection}
-     * @param uid UID of the client process
-     * @param pid PID of the client process
-     */
-    public abstract void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
-            int pid);
-
-    /**
-     * Called by the window manager service when a client process is being attached to the window
-     * manager service.
-     *
-     * <p>The caller must not have WindowManagerService lock.  This method internally acquires
-     * InputMethodManagerService lock.</p>
-     *
-     * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
-     *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
-     *               process
-     */
-    public abstract void removeClient(IInputMethodClient client);
-
-    /**
      * Called by the power manager to tell the input method manager whether it
      * should start watching for wake events.
      */
@@ -78,15 +44,6 @@
     public static final InputMethodManagerInternal NOP =
             new InputMethodManagerInternal() {
                 @Override
-                public void addClient(IInputMethodClient client, IInputContext inputContext,
-                        int uid, int pid) {
-                }
-
-                @Override
-                public void removeClient(IInputMethodClient client) {
-                }
-
-                @Override
                 public void setInteractive(boolean interactive) {
                 }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b2287ac..6f5f90a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -401,12 +401,28 @@
         }
     }
 
+    private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
+        private final InputMethodManagerService mImms;
+        private final IInputMethodClient mClient;
+
+        ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
+            mImms = imms;
+            mClient = client;
+        }
+
+        @Override
+        public void binderDied() {
+            mImms.removeClient(mClient);
+        }
+    }
+
     static final class ClientState {
         final IInputMethodClient client;
         final IInputContext inputContext;
         final int uid;
         final int pid;
         final InputBinding binding;
+        final ClientDeathRecipient clientDeathRecipient;
 
         boolean sessionRequested;
         SessionState curSession;
@@ -419,12 +435,13 @@
         }
 
         ClientState(IInputMethodClient _client, IInputContext _inputContext,
-                int _uid, int _pid) {
+                int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) {
             client = _client;
             inputContext = _inputContext;
             uid = _uid;
             pid = _pid;
             binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
+            clientDeathRecipient = _clientDeathRecipient;
         }
     }
 
@@ -1716,9 +1733,39 @@
         }
     }
 
-    void addClient(ClientState clientState) {
+    /**
+     * Called by each application process as a preparation to start interacting with
+     * {@link InputMethodManagerService}.
+     *
+     * <p>As a general principle, IPCs from the application process that take
+     * {@link InputMethodClient} will be rejected without this step.</p>
+     *
+     * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
+     *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
+     *               process
+     * @param inputContext communication channel for the dummy
+     *                     {@link android.view.inputmethod.InputConnection}
+     */
+    @Override
+    public void addClient(IInputMethodClient client, IInputContext inputContext) {
+        final int callerUid = Binder.getCallingUid();
+        final int callerPid = Binder.getCallingPid();
         synchronized (mMethodMap) {
-            mClients.put(clientState.client.asBinder(), clientState);
+            // TODO: Optimize this linear search.
+            for (ClientState state : mClients.values()) {
+                if (state.uid == callerUid && state.pid == callerPid) {
+                    throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+                            + " is already registered");
+                }
+            }
+            final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
+            try {
+                client.asBinder().linkToDeath(deathRecipient, 0);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+            mClients.put(client.asBinder(),
+                    new ClientState(client, inputContext, callerUid, callerPid, deathRecipient));
         }
     }
 
@@ -1726,6 +1773,7 @@
         synchronized (mMethodMap) {
             ClientState cs = mClients.remove(client.asBinder());
             if (cs != null) {
+                client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
                 clearClientSessionLocked(cs);
                 if (mCurClient == cs) {
                     if (mBoundToMethod) {
@@ -2536,7 +2584,11 @@
                     // We need to check if this is the current client with
                     // focus in the window manager, to allow this call to
                     // be made before input is started in it.
-                    if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+                    final ClientState cs = mClients.get(client.asBinder());
+                    if (cs == null) {
+                        throw new IllegalArgumentException("unknown client " + client.asBinder());
+                    }
+                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
                         Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
                         return false;
                     }
@@ -2616,7 +2668,11 @@
                     // We need to check if this is the current client with
                     // focus in the window manager, to allow this call to
                     // be made before input is started in it.
-                    if (!mWindowManagerInternal.inputMethodClientHasFocus(client)) {
+                    final ClientState cs = mClients.get(client.asBinder());
+                    if (cs == null) {
+                        throw new IllegalArgumentException("unknown client " + client.asBinder());
+                    }
+                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
                         if (DEBUG) {
                             Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
                         }
@@ -2734,7 +2790,7 @@
                             + client.asBinder());
                 }
 
-                if (!mWindowManagerInternal.inputMethodClientHasFocus(cs.client)) {
+                if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
                     // Check with the window manager to make sure this client actually
                     // has a window with focus.  If not, reject.  This is thread safe
                     // because if the focus changes some time before or after, the
@@ -4399,20 +4455,6 @@
         }
 
         @Override
-        public void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
-                int pid) {
-            // Work around Bug 113877122: We need to handle this synchronously.  Otherwise, some
-            // IMM binder calls from the client process before we register this client.
-            mService.addClient(new ClientState(client, inputContext, uid, pid));
-        }
-
-        @Override
-        public void removeClient(IInputMethodClient client) {
-            // Handle this synchronously to be consistent with addClient().
-            mService.removeClient(client);
-        }
-
-        @Override
         public void setInteractive(boolean interactive) {
             // Do everything in handler so as not to block the caller.
             mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 050a075..d326c22 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -20,6 +20,7 @@
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.os.UserHandle.USER_ALL;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -53,12 +54,15 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
@@ -72,6 +76,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
 
@@ -138,10 +143,6 @@
     // not mean that we are currently bound to said package/component.
     private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
 
-    // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
-    // user change).
-    private int[] mLastSeenProfileIds;
-
     // True if approved services are stored in xml, not settings.
     private boolean mUseXml;
 
@@ -300,7 +301,7 @@
                 Settings.Secure.putStringForUser(
                         mContext.getContentResolver(), element, value, userId);
                 loadAllowedComponentsFromSettings();
-                rebindServices(false);
+                rebindServices(false, userId);
             }
         }
     }
@@ -385,7 +386,7 @@
                 }
             }
         }
-        rebindServices(false);
+        rebindServices(false, USER_ALL);
     }
 
     protected void upgradeXml(final int xmlVersion, final int userId) {}
@@ -460,7 +461,7 @@
             }
         }
 
-        rebindServices(false);
+        rebindServices(false, userId);
     }
 
     private String getApprovedValue(String pkgOrComponent) {
@@ -578,7 +579,7 @@
 
             if (anyServicesInvolved) {
                 // make sure we're still bound to any of our services who may have just upgraded
-                rebindServices(false);
+                rebindServices(false, USER_ALL);
             }
         }
     }
@@ -586,21 +587,17 @@
     public void onUserRemoved(int user) {
         Slog.i(TAG, "Removing approved services for removed user " + user);
         mApproved.remove(user);
-        rebindServices(true);
+        rebindServices(true, user);
     }
 
     public void onUserSwitched(int user) {
         if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
-        if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
-            if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
-            return;
-        }
-        rebindServices(true);
+        rebindServices(true, user);
     }
 
     public void onUserUnlocked(int user) {
         if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
-        rebindServices(false);
+        rebindServices(false, user);
     }
 
     private ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
@@ -694,9 +691,10 @@
                 component.flattenToShortString());
 
         synchronized (mMutex) {
-            final int[] userIds = mUserProfiles.getCurrentProfileIds();
+            final IntArray userIds = mUserProfiles.getCurrentProfileIds();
 
-            for (int userId : userIds) {
+            for (int i = 0; i < userIds.size(); i++) {
+                final int userId = userIds.get(i);
                 if (enabled) {
                     if (isPackageOrComponentAllowed(component.toString(), userId)
                             || isPackageOrComponentAllowed(component.getPackageName(), userId)) {
@@ -838,20 +836,14 @@
         return false;
     }
 
-    /**
-     * Called whenever packages change, the user switches, or the secure setting
-     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
-     */
-    protected void rebindServices(boolean forceRebind) {
-        if (DEBUG) Slog.d(TAG, "rebindServices");
-        final int[] userIds = mUserProfiles.getCurrentProfileIds();
-        final int nUserIds = userIds.length;
-
+    @VisibleForTesting
+    protected SparseArray<ArraySet<ComponentName>> getAllowedComponents(IntArray userIds) {
+        final int nUserIds = userIds.size();
         final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
 
         for (int i = 0; i < nUserIds; ++i) {
-            final int userId = userIds[i];
-            final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userIds[i]);
+            final int userId = userIds.get(i);
+            final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
             if (approvedLists != null) {
                 final int N = approvedLists.size();
                 for (int j = 0; j < N; j++) {
@@ -865,67 +857,125 @@
                 }
             }
         }
+        return componentsByUser;
+    }
 
-        final ArrayList<ManagedServiceInfo> removableBoundServices = new ArrayList<>();
-        final SparseArray<Set<ComponentName>> toAdd = new SparseArray<>();
-
-        synchronized (mMutex) {
-            // Rebind to non-system services if user switched
-            for (ManagedServiceInfo service : mServices) {
-                if (!service.isSystem && !service.isGuest(this)) {
-                    removableBoundServices.add(service);
-                }
-            }
-
-            mEnabledServicesForCurrentProfiles.clear();
-            mEnabledServicesPackageNames.clear();
-
-            for (int i = 0; i < nUserIds; ++i) {
-                // decode the list of components
-                final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
-                if (null == userComponents) {
-                    toAdd.put(userIds[i], new ArraySet<>());
-                    continue;
-                }
-
-                final Set<ComponentName> add = new HashSet<>(userComponents);
-                add.removeAll(mSnoozingForCurrentProfiles);
-
-                toAdd.put(userIds[i], add);
-
-                mEnabledServicesForCurrentProfiles.addAll(userComponents);
-
-                for (int j = 0; j < userComponents.size(); j++) {
-                    final ComponentName component = userComponents.valueAt(j);
-                    mEnabledServicesPackageNames.add(component.getPackageName());
-                }
-            }
-        }
-
-        for (ManagedServiceInfo info : removableBoundServices) {
-            final ComponentName component = info.component;
-            final int oldUser = info.userid;
-            final Set<ComponentName> allowedComponents = toAdd.get(info.userid);
-            if (allowedComponents != null) {
-                if (allowedComponents.contains(component) && !forceRebind) {
-                    // Already bound, don't need to bind again.
-                    allowedComponents.remove(component);
-                } else {
-                    // No longer allowed to be bound, or must rebind.
-                    Slog.v(TAG, "disabling " + getCaption() + " for user "
-                            + oldUser + ": " + component);
-                    unregisterService(component, oldUser);
-                }
-            }
-        }
+    @GuardedBy("mMutex")
+    protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
+            final IntArray activeUsers,
+            SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
+        mEnabledServicesForCurrentProfiles.clear();
+        mEnabledServicesPackageNames.clear();
+        final int nUserIds = activeUsers.size();
 
         for (int i = 0; i < nUserIds; ++i) {
-            final Set<ComponentName> add = toAdd.get(userIds[i]);
+            // decode the list of components
+            final int userId = activeUsers.get(i);
+            final ArraySet<ComponentName> userComponents = approvedComponentsByUser.get(userId);
+            if (null == userComponents) {
+                componentsToBind.put(userId, new ArraySet<>());
+                continue;
+            }
+
+            final Set<ComponentName> add = new HashSet<>(userComponents);
+            add.removeAll(mSnoozingForCurrentProfiles);
+
+            componentsToBind.put(userId, add);
+
+            mEnabledServicesForCurrentProfiles.addAll(userComponents);
+
+            for (int j = 0; j < userComponents.size(); j++) {
+                final ComponentName component = userComponents.valueAt(j);
+                mEnabledServicesPackageNames.add(component.getPackageName());
+            }
+        }
+    }
+
+    @GuardedBy("mMutex")
+    protected Set<ManagedServiceInfo> getRemovableConnectedServices() {
+        final Set<ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+        for (ManagedServiceInfo service : mServices) {
+            if (!service.isSystem && !service.isGuest(this)) {
+                removableBoundServices.add(service);
+            }
+        }
+        return removableBoundServices;
+    }
+
+    protected void populateComponentsToUnbind(
+            boolean forceRebind,
+            Set<ManagedServiceInfo> removableBoundServices,
+            SparseArray<Set<ComponentName>> allowedComponentsToBind,
+            SparseArray<Set<ComponentName>> componentsToUnbind) {
+        for (ManagedServiceInfo info : removableBoundServices) {
+            final Set<ComponentName> allowedComponents = allowedComponentsToBind.get(info.userid);
+            if (allowedComponents != null) {
+                if (forceRebind || !allowedComponents.contains(info.component)) {
+                    Set<ComponentName> toUnbind =
+                            componentsToUnbind.get(info.userid, new ArraySet<>());
+                    toUnbind.add(info.component);
+                    componentsToUnbind.put(info.userid, toUnbind);
+                }
+            }
+        }
+    }
+
+    /**
+     * Called whenever packages change, the user switches, or the secure setting
+     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+     */
+    protected void rebindServices(boolean forceRebind, int userToRebind) {
+        if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind);
+        IntArray userIds = mUserProfiles.getCurrentProfileIds();
+        if (userToRebind != USER_ALL) {
+            userIds = new IntArray(1);
+            userIds.add(userToRebind);
+        }
+
+        final SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+        final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        synchronized (mMutex) {
+            final SparseArray<ArraySet<ComponentName>> approvedComponentsByUser =
+                    getAllowedComponents(userIds);
+            final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+
+            // Filter approvedComponentsByUser to collect all of the components that are allowed
+            // for the currently active user(s).
+            populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+
+            // For every current non-system connection, disconnect services that are no longer
+            // approved, or ALL services if we are force rebinding
+            populateComponentsToUnbind(
+                    forceRebind, removableBoundServices, componentsToBind, componentsToUnbind);
+        }
+
+        unbindFromServices(componentsToUnbind);
+        bindToServices(componentsToBind);
+    }
+
+    protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
+        for (int i = 0; i < componentsToUnbind.size(); i++) {
+            final int userId = componentsToUnbind.keyAt(i);
+            final Set<ComponentName> removableComponents = componentsToUnbind.get(userId);
+            for (ComponentName cn : removableComponents) {
+                // No longer allowed to be bound, or must rebind.
+                Slog.v(TAG, "disabling " + getCaption() + " for user " + userId + ": " + cn);
+                unregisterService(cn, userId);
+            }
+        }
+    }
+
+    // Attempt to bind to services, skipping those that cannot be found or lack the permission.
+    private void bindToServices(SparseArray<Set<ComponentName>> componentsToBind) {
+        for (int i = 0; i < componentsToBind.size(); i++) {
+            final int userId = componentsToBind.keyAt(i);
+            final Set<ComponentName> add = componentsToBind.get(userId);
             for (ComponentName component : add) {
                 try {
                     ServiceInfo info = mPm.getServiceInfo(component,
                             PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
                     if (info == null) {
                         Slog.w(TAG, "Not binding " + getCaption() + " service " + component
                                 + ": service not found");
@@ -937,15 +987,13 @@
                         continue;
                     }
                     Slog.v(TAG,
-                            "enabling " + getCaption() + " for " + userIds[i] + ": " + component);
-                    registerService(component, userIds[i]);
+                            "enabling " + getCaption() + " for " + userId + ": " + component);
+                    registerService(component, userId);
                 } catch (RemoteException e) {
                     e.rethrowFromSystemServer();
                 }
             }
         }
-
-        mLastSeenProfileIds = userIds;
     }
 
     /**
@@ -1022,7 +1070,7 @@
 
                 @Override
                 public void onServiceConnected(ComponentName name, IBinder binder) {
-                    Slog.v(TAG, getCaption() + " service connected: " + name);
+                    Slog.v(TAG,  userid + " " + getCaption() + " service connected: " + name);
                     boolean added = false;
                     ManagedServiceInfo info = null;
                     synchronized (mMutex) {
@@ -1044,12 +1092,12 @@
 
                 @Override
                 public void onServiceDisconnected(ComponentName name) {
-                    Slog.v(TAG, getCaption() + " connection lost: " + name);
+                    Slog.v(TAG, userid + " " + getCaption() + " connection lost: " + name);
                 }
 
                 @Override
                 public void onBindingDied(ComponentName name) {
-                    Slog.w(TAG, getCaption() + " binding died: " + name);
+                    Slog.w(TAG,  userid + " " + getCaption() + " binding died: " + name);
                     synchronized (mMutex) {
                         unbindService(this, name, userid);
                         if (!mServicesRebinding.contains(servicesBindingTag)) {
@@ -1061,8 +1109,8 @@
                                     }
                                }, ON_BINDING_DIED_REBIND_DELAY_MS);
                         } else {
-                            Slog.v(TAG, getCaption() + " not rebinding as "
-                                    + "a previous rebind attempt was made: " + name);
+                            Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+                                    + " as a previous rebind attempt was made: " + name);
                         }
                     }
                 }
@@ -1072,7 +1120,8 @@
                     getBindFlags(),
                     new UserHandle(userid))) {
                 mServicesBound.remove(servicesBindingTag);
-                Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+                Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent
+                        + " in user " + userid);
                 return;
             }
         } catch (SecurityException ex) {
@@ -1236,9 +1285,9 @@
             if (!isEnabledForCurrentProfiles()) {
                 return false;
             }
-            if (this.userid == UserHandle.USER_ALL) return true;
+            if (this.userid == USER_ALL) return true;
             if (this.isSystem) return true;
-            if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+            if (nid == USER_ALL || nid == this.userid) return true;
             return supportsProfiles()
                     && mUserProfiles.isCurrentProfile(nid)
                     && isPermittedForProfile(nid);
@@ -1284,6 +1333,24 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ManagedServiceInfo that = (ManagedServiceInfo) o;
+            return userid == that.userid
+                    && isSystem == that.isSystem
+                    && targetSdkVersion == that.targetSdkVersion
+                    && Objects.equals(service, that.service)
+                    && Objects.equals(component, that.component)
+                    && Objects.equals(connection, that.connection);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(service, component, userid, isSystem, connection, targetSdkVersion);
+        }
     }
 
     /** convenience method for looking in mEnabledServicesForCurrentProfiles */
@@ -1309,12 +1376,15 @@
             }
         }
 
-        public int[] getCurrentProfileIds() {
+        /**
+         * Returns the currently active users (generally one user and its work profile).
+         */
+        public IntArray getCurrentProfileIds() {
             synchronized (mCurrentProfiles) {
-                int[] users = new int[mCurrentProfiles.size()];
+                IntArray users = new IntArray(mCurrentProfiles.size());
                 final int N = mCurrentProfiles.size();
                 for (int i = 0; i < N; ++i) {
-                    users[i] = mCurrentProfiles.keyAt(i);
+                    users.add(mCurrentProfiles.keyAt(i));
                 }
                 return users;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 33f0172..e53eeb0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -178,6 +178,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1566,7 +1567,7 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
-        getContext().registerReceiver(mIntentReceiver, filter);
+        getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
 
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -1698,10 +1699,10 @@
                     UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
                     null);
             if (isUidSystemOrPhone(uid)) {
-                int[] profileIds = mUserProfiles.getCurrentProfileIds();
-                int N = profileIds.length;
+                IntArray profileIds = mUserProfiles.getCurrentProfileIds();
+                int N = profileIds.size();
                 for (int i = 0; i < N; i++) {
-                    int profileId = profileIds[i];
+                    int profileId = profileIds.get(i);
                     cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
                             profileId, REASON_CHANNEL_BANNED,
                             null);
@@ -6691,7 +6692,9 @@
         @Override
         public void onUserUnlocked(int user) {
             if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
-            rebindServices(true);
+            // force rebind the assistant, as it might be keeping its own state in user locked
+            // storage
+            rebindServices(true, user);
         }
 
         protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index a178a52..2b581d6 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -15,17 +15,8 @@
  */
 package com.android.server.notification;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.NonNull;
 import android.app.AlarmManager;
-import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -37,9 +28,18 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -105,12 +105,12 @@
 
     protected @NonNull List<NotificationRecord> getSnoozed() {
         List<NotificationRecord> snoozedForUser = new ArrayList<>();
-        int[] userIds = mUserProfiles.getCurrentProfileIds();
+        IntArray userIds = mUserProfiles.getCurrentProfileIds();
         if (userIds != null) {
-            final int N = userIds.length;
+            final int N = userIds.size();
             for (int i = 0; i < N; i++) {
                 final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
-                        mSnoozedNotifications.get(userIds[i]);
+                        mSnoozedNotifications.get(userIds.get(i));
                 if (snoozedPkgs != null) {
                     final int M = snoozedPkgs.size();
                     for (int j = 0; j < M; j++) {
@@ -179,7 +179,7 @@
     protected boolean cancel(int userId, boolean includeCurrentProfiles) {
         int[] userIds = {userId};
         if (includeCurrentProfiles) {
-            userIds = mUserProfiles.getCurrentProfileIds();
+            userIds = mUserProfiles.getCurrentProfileIds().toArray();
         }
         final int N = userIds.length;
         for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 5bf323a..3b11525 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -1049,7 +1049,7 @@
                                 (other != null && other.getComponentName() != null)
                                         ? other.getComponentName().getPackageName() : "?";
                         // if we're installing over the same already-installed package, this is ok
-                        if (otherPackageName != pkg.packageName) {
+                        if (!otherPackageName.equals(pkg.packageName)) {
                             throw new PackageManagerException(
                                     INSTALL_FAILED_CONFLICTING_PROVIDER,
                                     "Can't install because provider name " + names[j]
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index d9eb7e8..3c9dd63 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -946,7 +946,7 @@
     }
 
     private boolean isSystemPackage(String packageName) {
-        return isSystemPackage(getSystemPackageInfo(packageName));
+        return isSystemPackage(getPackageInfo(packageName));
     }
 
     private boolean isSystemPackage(PackageInfo pkg) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3b19beb..91fd8d0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -125,6 +125,7 @@
 import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
+
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -631,9 +632,11 @@
 
     InputConsumer mInputConsumer = null;
 
-    private final WindowFrames mWindowFrames = new WindowFrames();
-    private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
-    private static final Rect mTmpRect = new Rect();
+    private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
+    private static final Rect sTmpRect = new Rect();
+    private static final Rect sTmpDockedFrame = new Rect();
+    private static final Rect sTmpNavFrame = new Rect();
+    private static final Rect sTmpLastParentFrame = new Rect();
 
     WindowState mTopFullscreenOpaqueWindowState;
     WindowState mTopFullscreenOpaqueOrDimmingWindowState;
@@ -4308,9 +4311,6 @@
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
 
-        mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
-        mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
-
         if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
             // For purposes of putting out fake window up to steal focus, we will
             // drive nav being hidden only by whether it is requested.
@@ -4377,12 +4377,8 @@
             return;
         }
 
-        mTmpRect.setEmpty();
-        mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
-                displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
-                displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
-                mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
-                displayFrames.mDock /* outsetFrame */);
+        sTmpRect.setEmpty();
+        sTmpDockedFrame.set(displayFrames.mDock);
 
         final int displayId = displayFrames.mDisplayId;
         final Rect dockFrame = displayFrames.mDock;
@@ -4396,7 +4392,13 @@
                 continue;
             }
 
-            w.computeFrameLw(mWindowFrames);
+            w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */,
+                    sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */,
+                    sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */,
+                    sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */,
+                    sTmpDockedFrame /* outsetFrame */);
+            w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+            w.computeFrameLw();
             final Rect frame = w.getFrameLw();
 
             if (frame.left <= 0 && frame.top <= 0) {
@@ -4448,17 +4450,17 @@
             return false;
         }
         // apply any navigation bar insets
-        mTmpRect.setEmpty();
-        mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+        sTmpRect.setEmpty();
+        mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
                 displayFrames.mUnrestricted /* displayFrame */,
                 displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
-                displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+                displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
                 displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
-
+        mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
         mStatusBarLayer = mStatusBar.getSurfaceLayer();
 
         // Let the status bar determine its size.
-        mStatusBar.computeFrameLw(mWindowFrames);
+        mStatusBar.computeFrameLw();
 
         // For layout, the status bar is always at the top with our fixed height.
         displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4468,11 +4470,11 @@
                 displayFrames.mDisplayCutoutSafe.top);
 
         // Tell the bar controller where the collapsed status bar content is
-        mTmpRect.set(mStatusBar.getContentFrameLw());
-        mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
-        mTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
-        mTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
-        mStatusBarController.setContentFrame(mTmpRect);
+        sTmpRect.set(mStatusBar.getContentFrameLw());
+        sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+        sTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
+        sTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
+        mStatusBarController.setContentFrame(sTmpRect);
 
         boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
         boolean statusBarTranslucent = (sysui
@@ -4512,7 +4514,7 @@
             return false;
         }
 
-        final Rect navigationFrame = mWindowFrames.mParentFrame;
+        final Rect navigationFrame = sTmpNavFrame;
         boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
         // Force the navigation bar to its appropriate place and size. We need to do this directly,
         // instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4523,7 +4525,7 @@
         final Rect dockFrame = displayFrames.mDock;
         mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
 
-        final Rect cutoutSafeUnrestricted = mTmpRect;
+        final Rect cutoutSafeUnrestricted = sTmpRect;
         cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
         cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
 
@@ -4605,15 +4607,15 @@
         displayFrames.mContent.set(dockFrame);
         mStatusBarLayer = mNavigationBar.getSurfaceLayer();
         // And compute the final frame.
-        mTmpRect.setEmpty();
-        mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+        sTmpRect.setEmpty();
+        mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */,
                 navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
                 displayFrames.mDisplayCutoutSafe /* contentFrame */,
-                navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+                navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
                 navigationFrame /* stableFrame */,
                 displayFrames.mDisplayCutoutSafe /* outsetFrame */);
-
-        mNavigationBar.computeFrameLw(mWindowFrames);
+        mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+        mNavigationBar.computeFrameLw();
         mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
 
         if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
@@ -4744,17 +4746,20 @@
         final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
         final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
 
-        final Rect pf = mWindowFrames.mParentFrame;
-        final Rect df = mWindowFrames.mDisplayFrame;
-        final Rect of = mWindowFrames.mOverscanFrame;
-        final Rect cf = mWindowFrames.mContentFrame;
-        final Rect vf = mWindowFrames.mVisibleFrame;
-        final Rect dcf = mWindowFrames.mDecorFrame;
-        final Rect sf = mWindowFrames.mStableFrame;
+        final WindowFrames windowFrames = win.getWindowFrames();
+
+        windowFrames.setHasOutsets(false);
+        sTmpLastParentFrame.set(windowFrames.mParentFrame);
+        final Rect pf = windowFrames.mParentFrame;
+        final Rect df = windowFrames.mDisplayFrame;
+        final Rect of = windowFrames.mOverscanFrame;
+        final Rect cf = windowFrames.mContentFrame;
+        final Rect vf = windowFrames.mVisibleFrame;
+        final Rect dcf = windowFrames.mDecorFrame;
+        final Rect sf = windowFrames.mStableFrame;
         dcf.setEmpty();
-        mWindowFrames.mOutsetFrame.setEmpty();
-        mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
-        mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+        windowFrames.setParentFrameWasClippedByDisplayCutout(false);
+        windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
 
         final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
                 && mNavigationBar != null && mNavigationBar.isVisibleLw());
@@ -4774,7 +4779,7 @@
             cf.set(displayFrames.mDock);
             of.set(displayFrames.mDock);
             df.set(displayFrames.mDock);
-            pf.set(displayFrames.mDock);
+            windowFrames.mParentFrame.set(displayFrames.mDock);
             // IM dock windows layout below the nav bar...
             pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
             // ...with content insets above the nav bar
@@ -4876,9 +4881,7 @@
                                 ? displayFrames.mRestricted.bottom
                                 : displayFrames.mUnrestricted.bottom;
 
-                        if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
-                                        "Laying out status bar window: (%d,%d - %d,%d)",
-                                        pf.left, pf.top, pf.right, pf.bottom));
+                        if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
                     } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
                             && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
                         // Asking to layout into the overscan region, so give it that pure
@@ -4950,17 +4953,13 @@
                         pf.bottom = df.bottom = of.bottom = cf.bottom =
                                 displayFrames.mRestricted.bottom;
                     }
-                    if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
-                            "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
-                            pf.left, pf.top, pf.right, pf.bottom));
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
                 } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
                     // The navigation bar has Real Ultimate Power.
                     of.set(displayFrames.mUnrestricted);
                     df.set(displayFrames.mUnrestricted);
                     pf.set(displayFrames.mUnrestricted);
-                    if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
-                                    "Laying out navigation bar window: (%d,%d - %d,%d)",
-                                    pf.left, pf.top, pf.right, pf.bottom));
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
                 } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
                         && ((fl & FLAG_FULLSCREEN) != 0)) {
                     // Fullscreen secure system overlays get what they ask for. Screenshot region
@@ -5087,7 +5086,7 @@
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
-            final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
+            final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
             displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
             if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
                     && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
@@ -5122,9 +5121,9 @@
             // They will later be cropped or shifted using the displayFrame in WindowState,
             // which prevents overlap with the DisplayCutout.
             if (!attachedInParent && !floatingInScreenWindow) {
-                mTmpRect.set(pf);
+                sTmpRect.set(pf);
                 pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
-                mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf));
+                windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
             }
             // Make sure that NO_LIMITS windows clipped to the display don't extend under the
             // cutout.
@@ -5152,8 +5151,9 @@
         // apply the outsets to floating dialogs, because they wouldn't make sense there.
         final boolean useOutsets = shouldUseOutsets(attrs, fl);
         if (isDefaultDisplay && useOutsets) {
-            final Rect osf = mWindowFrames.mOutsetFrame;
+            final Rect osf = windowFrames.mOutsetFrame;
             osf.set(cf.left, cf.top, cf.right, cf.bottom);
+            windowFrames.setHasOutsets(true);
             int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
             if (outset > 0) {
                 int rotation = displayFrames.mRotation;
@@ -5180,9 +5180,13 @@
                 + " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
                 + " dcf=" + dcf.toShortString()
                 + " sf=" + sf.toShortString()
-                + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
+                + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win);
 
-        win.computeFrameLw(mWindowFrames);
+        if (!sTmpLastParentFrame.equals(pf)) {
+            windowFrames.setContentChanged(true);
+        }
+
+        win.computeFrameLw();
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
         if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
new file mode 100644
index 0000000..e212b04
--- /dev/null
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -0,0 +1,50 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "WmTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        }
+      ]
+    },
+    {
+      "name": "WmTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.policy."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b55adeb..1fcdd63 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -63,12 +63,10 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -95,7 +93,6 @@
 import com.android.server.wm.DisplayFrames;
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowFrames;
-import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -201,10 +198,8 @@
          * getFrame() if so desired.  Must be called with the window manager
          * lock held.
          *
-         * @param windowFrames Container for all the window frames that affect how the window is
-         *                     laid out.
          */
-        public void computeFrameLw(WindowFrames windowFrames);
+        public void computeFrameLw();
 
         /**
          * Retrieve the current frame of the window that has been assigned by
@@ -477,6 +472,11 @@
          * Writes {@link com.android.server.wm.IdentifierProto} to stream.
          */
         void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
+
+        /**
+         * @return The {@link WindowFrames} associated with this {@link WindowState}
+         */
+        WindowFrames getWindowFrames();
     }
 
     /**
@@ -1167,7 +1167,6 @@
     default void layoutWindowLw(
             WindowState win, WindowState attached, DisplayFrames displayFrames) {}
 
-
     /**
      * Return the layout hints for a newly added window. These values are computed on the
      * most recent layout, so they are not guaranteed to be correct.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5981ab0..68e636a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,6 +49,7 @@
 import android.util.Slog;
 import android.util.StatsLog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -75,7 +76,8 @@
  * tell the system when we go to sleep so that it can lock the keyguard if needed.
  * </p>
  */
-final class Notifier {
+@VisibleForTesting
+public class Notifier {
     private static final String TAG = "PowerManagerNotifier";
 
     private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bdf12ca..9f6b3dd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -64,7 +64,6 @@
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
 import android.service.vr.IVrManager;
@@ -84,7 +83,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LockGuard;
@@ -230,6 +228,10 @@
     private final BatterySaverController mBatterySaverController;
     private final BatterySaverStateMachine mBatterySaverStateMachine;
     private final BatterySavingStats mBatterySavingStats;
+    private final BinderService mBinderService;
+    private final LocalService mLocalService;
+    private final NativeWrapper mNativeWrapper;
+    private final Injector mInjector;
 
     private LightsManager mLightsManager;
     private BatteryManagerInternal mBatteryManagerInternal;
@@ -636,10 +638,76 @@
         }
     }
 
+    /**
+     * Wrapper around the static-native methods of PowerManagerService.
+     *
+     * This class exists to allow us to mock static native methods in our tests. If mocking static
+     * methods becomes easier than this in the future, we can delete this class.
+     */
+    @VisibleForTesting
+    public static class NativeWrapper {
+        /** Wrapper for PowerManager.nativeInit */
+        public void nativeInit(PowerManagerService service) {
+            service.nativeInit();
+        }
+
+        /** Wrapper for PowerManager.nativeAcquireSuspectBlocker */
+        public void nativeAcquireSuspendBlocker(String name) {
+            PowerManagerService.nativeAcquireSuspendBlocker(name);
+        }
+
+        /** Wrapper for PowerManager.nativeReleaseSuspendBlocker */
+        public void nativeReleaseSuspendBlocker(String name) {
+            PowerManagerService.nativeReleaseSuspendBlocker(name);
+        }
+
+        /** Wrapper for PowerManager.nativeSetInteractive */
+        public void nativeSetInteractive(boolean enable) {
+            PowerManagerService.nativeSetInteractive(enable);
+        }
+
+        /** Wrapper for PowerManager.nativeSetAutoSuspend */
+        public void nativeSetAutoSuspend(boolean enable) {
+            PowerManagerService.nativeSetAutoSuspend(enable);
+        }
+
+        /** Wrapper for PowerManager.nativeSendPowerHint */
+        public void nativeSendPowerHint(int hintId, int data) {
+            PowerManagerService.nativeSendPowerHint(hintId, data);
+        }
+
+        /** Wrapper for PowerManager.nativeSetFeature */
+        public void nativeSetFeature(int featureId, int data) {
+            PowerManagerService.nativeSetFeature(featureId, data);
+        }
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+            return new Notifier(looper, context, batteryStats, suspendBlocker, policy);
+        }
+
+        SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+            SuspendBlocker suspendBlocker = service.new SuspendBlockerImpl(name);
+            service.mSuspendBlockers.add(suspendBlocker);
+            return suspendBlocker;
+        }
+
+        BatterySaverPolicy createBatterySaverPolicy(
+                Object lock, Context context, BatterySavingStats batterySavingStats) {
+            return new BatterySaverPolicy(lock, context, batterySavingStats);
+        }
+
+        NativeWrapper createNativeWrapper() {
+            return new NativeWrapper();
+        }
+    }
+
     final Constants mConstants;
 
     private native void nativeInit();
-
     private static native void nativeAcquireSuspendBlocker(String name);
     private static native void nativeReleaseSuspendBlocker(String name);
     private static native void nativeSetInteractive(boolean enable);
@@ -648,8 +716,19 @@
     private static native void nativeSetFeature(int featureId, int data);
 
     public PowerManagerService(Context context) {
+        this(context, new Injector());
+    }
+
+    @VisibleForTesting
+    PowerManagerService(Context context, Injector injector) {
         super(context);
+
         mContext = context;
+        mBinderService = new BinderService();
+        mLocalService = new LocalService();
+        mNativeWrapper = injector.createNativeWrapper();
+        mInjector = injector;
+
         mHandlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
         mHandlerThread.start();
@@ -658,57 +737,40 @@
         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
 
         mBatterySavingStats = new BatterySavingStats(mLock);
-        mBatterySaverPolicy = new BatterySaverPolicy(mLock, mContext, mBatterySavingStats);
+        mBatterySaverPolicy =
+                mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
         mBatterySaverController = new BatterySaverController(mLock, mContext,
-                BackgroundThread.get().getLooper(), mBatterySaverPolicy, mBatterySavingStats);
+                BackgroundThread.get().getLooper(), mBatterySaverPolicy,
+                mBatterySavingStats);
         mBatterySaverStateMachine = new BatterySaverStateMachine(
                 mLock, mContext, mBatterySaverController);
 
         synchronized (mLock) {
-            mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
-            mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
-            mDisplaySuspendBlocker.acquire();
-            mHoldingDisplaySuspendBlocker = true;
+            mWakeLockSuspendBlocker =
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
+            mDisplaySuspendBlocker =
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+            if (mDisplaySuspendBlocker != null) {
+                mDisplaySuspendBlocker.acquire();
+                mHoldingDisplaySuspendBlocker = true;
+            }
             mHalAutoSuspendModeEnabled = false;
             mHalInteractiveModeEnabled = true;
 
             mWakefulness = WAKEFULNESS_AWAKE;
-
             sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
 
-            nativeInit();
-            nativeSetAutoSuspend(false);
-            nativeSetInteractive(true);
-            nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
+            mNativeWrapper.nativeInit(this);
+            mNativeWrapper.nativeSetAutoSuspend(false);
+            mNativeWrapper.nativeSetInteractive(true);
+            mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
         }
     }
 
-    @VisibleForTesting
-    PowerManagerService(Context context, BatterySaverPolicy batterySaverPolicy) {
-        super(context);
-
-        mContext = context;
-        mHandlerThread = new ServiceThread(TAG,
-                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
-        mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
-        mConstants = new Constants(mHandler);
-        mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
-        mDisplaySuspendBlocker = null;
-        mWakeLockSuspendBlocker = null;
-
-        mBatterySavingStats = new BatterySavingStats(mLock);
-        mBatterySaverPolicy = batterySaverPolicy;
-        mBatterySaverController = new BatterySaverController(mLock, context,
-                BackgroundThread.getHandler().getLooper(), batterySaverPolicy, mBatterySavingStats);
-        mBatterySaverStateMachine = new BatterySaverStateMachine(
-                mLock, mContext, mBatterySaverController);
-    }
-
     @Override
     public void onStart() {
-        publishBinderService(Context.POWER_SERVICE, new BinderService());
-        publishLocalService(PowerManagerInternal.class, new LocalService());
+        publishBinderService(Context.POWER_SERVICE, mBinderService);
+        publishLocalService(PowerManagerInternal.class, mLocalService);
 
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
@@ -752,11 +814,13 @@
             // The notifier runs on the system server's main looper so as not to interfere
             // with the animations and other critical functions of the power manager.
             mBatteryStats = BatteryStatsService.getService();
-            mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
-                    createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
+            mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
+                    mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
+                    mPolicy);
 
             mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
-                    createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
+                    mInjector.createSuspendBlocker(
+                            this, "PowerManagerService.WirelessChargerDetector"),
                     mHandler);
             mSettingsObserver = new SettingsObserver(mHandler);
 
@@ -824,7 +888,7 @@
         resolver.registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.DEVICE_DEMO_MODE),
                 false, mSettingsObserver, UserHandle.USER_SYSTEM);
-        IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE);
+        IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE));
         if (vrManager != null) {
             try {
                 vrManager.registerListener(mVrStateCallbacks);
@@ -927,7 +991,8 @@
                             UserHandle.USER_CURRENT) != 0;
             if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
                 mDoubleTapWakeEnabled = doubleTapWakeEnabled;
-                nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+                mNativeWrapper.nativeSetFeature(
+                        POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
             }
         }
 
@@ -1509,6 +1574,11 @@
         }
     }
 
+    @VisibleForTesting
+    int getWakefulness() {
+        return mWakefulness;
+    }
+
     /**
      * Logs the time the device would have spent awake before user activity timeout,
      * had the system not been told the user was inactive.
@@ -2640,7 +2710,7 @@
             mHalAutoSuspendModeEnabled = enable;
             Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");
             try {
-                nativeSetAutoSuspend(enable);
+                mNativeWrapper.nativeSetAutoSuspend(enable);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_POWER);
             }
@@ -2655,7 +2725,7 @@
             mHalInteractiveModeEnabled = enable;
             Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalInteractive(" + enable + ")");
             try {
-                nativeSetInteractive(enable);
+                mNativeWrapper.nativeSetInteractive(enable);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_POWER);
             }
@@ -3127,7 +3197,7 @@
                 break;
         }
 
-        nativeSendPowerHint(hintId, data);
+        mNativeWrapper.nativeSendPowerHint(hintId, data);
     }
 
     /**
@@ -3718,12 +3788,6 @@
         proto.flush();
     }
 
-    private SuspendBlocker createSuspendBlockerLocked(String name) {
-        SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
-        mSuspendBlockers.add(suspendBlocker);
-        return suspendBlocker;
-    }
-
     private void incrementBootCount() {
         synchronized (mLock) {
             int count;
@@ -4022,7 +4086,7 @@
                     Slog.wtf(TAG, "Suspend blocker \"" + mName
                             + "\" was finalized without being released!");
                     mReferenceCount = 0;
-                    nativeReleaseSuspendBlocker(mName);
+                    mNativeWrapper.nativeReleaseSuspendBlocker(mName);
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                 }
             } finally {
@@ -4039,7 +4103,7 @@
                         Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
                     }
                     Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
-                    nativeAcquireSuspendBlocker(mName);
+                    mNativeWrapper.nativeAcquireSuspendBlocker(mName);
                 }
             }
         }
@@ -4052,7 +4116,7 @@
                     if (DEBUG_SPEW) {
                         Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
                     }
-                    nativeReleaseSuspendBlocker(mName);
+                    mNativeWrapper.nativeReleaseSuspendBlocker(mName);
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                 } else if (mReferenceCount < 0) {
                     Slog.wtf(TAG, "Suspend blocker \"" + mName
@@ -4090,7 +4154,8 @@
         }
     }
 
-    private final class BinderService extends IPowerManager.Stub {
+    @VisibleForTesting
+    final class BinderService extends IPowerManager.Stub {
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
@@ -4592,6 +4657,16 @@
     }
 
     @VisibleForTesting
+    BinderService getBinderServiceInstance() {
+        return mBinderService;
+    }
+
+    @VisibleForTesting
+    LocalService getLocalServiceInstance() {
+        return mLocalService;
+    }
+
+    @VisibleForTesting
     // lastRebootReasonProperty argument to permit testing
     int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
         String line = SystemProperties.get(lastRebootReasonProperty);
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 180f343..9f69702 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -23,11 +23,11 @@
 import android.os.SharedMemory;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.util.Pair;
+import android.util.Slog;
 import android.util.apk.ApkSignatureVerifier;
 import android.util.apk.ByteBufferFactory;
 import android.util.apk.SignatureNotFoundException;
-import android.util.Pair;
-import android.util.Slog;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -85,7 +85,7 @@
      */
     public static byte[] generateFsverityRootHash(@NonNull String apkPath)
             throws NoSuchAlgorithmException, DigestException, IOException {
-        return ApkSignatureVerifier.generateFsverityRootHash(apkPath);
+        return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
     }
 
     /**
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 444ac2c..bfa03ca 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -123,7 +123,7 @@
     public static final String CONFIG_DIR = "/data/misc/stats-service";
 
     static final String TAG = "StatsCompanionService";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = true;
 
     public static final int CODE_DATA_BROADCAST = 1;
     public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -254,34 +254,38 @@
 
     @Override
     public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
-                                        long subscriptionId, long subscriptionRuleId,
-                                        String[] cookies,
-                                        StatsDimensionsValue dimensionsValue) {
+            long subscriptionId, long subscriptionRuleId, String[] cookies,
+            StatsDimensionsValue dimensionsValue) {
         enforceCallingPermission();
         IntentSender intentSender = new IntentSender(intentSenderBinder);
-        Intent intent = new Intent()
-                .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
-                .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
-                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
-                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
-                .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+        Intent intent =
+                new Intent()
+                        .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+                        .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
+                        .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+                        .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
+                        .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
 
         ArrayList<String> cookieList = new ArrayList<>(cookies.length);
-        for (String cookie : cookies) { cookieList.add(cookie); }
+        for (String cookie : cookies) {
+            cookieList.add(cookie);
+        }
         intent.putStringArrayListExtra(
                 StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
 
         if (DEBUG) {
-            Slog.d(TAG, String.format(
-                    "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
-                    configUid, configKey, subscriptionId, subscriptionRuleId,
-                    Arrays.toString(cookies), dimensionsValue));
+            Slog.d(TAG,
+                    String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+                            configUid, configKey, subscriptionId, subscriptionRuleId,
+                            Arrays.toString(cookies),
+                            dimensionsValue));
         }
         try {
             intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
         } catch (IntentSender.SendIntentException e) {
-            Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
-                    + "; presumably it had been cancelled.");
+            Slog.w(TAG,
+                    "Unable to send using IntentSender from uid " + configUid
+                            + "; presumably it had been cancelled.");
         }
     }
 
@@ -318,7 +322,7 @@
         // Add in all the apps for every user/profile.
         for (UserInfo profile : users) {
             List<PackageInfo> pi =
-                pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
+                    pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
             for (int j = 0; j < pi.size(); j++) {
                 if (pi.get(j).applicationInfo != null) {
                     uids.add(pi.get(j).applicationInfo.uid);
@@ -403,8 +407,9 @@
     public final static class PullingAlarmListener implements OnAlarmListener {
         @Override
         public void onAlarm() {
-            if (DEBUG)
+            if (DEBUG) {
                 Slog.d(TAG, "Time to poll something.");
+            }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
                     Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
@@ -423,8 +428,9 @@
     public final static class PeriodicAlarmListener implements OnAlarmListener {
         @Override
         public void onAlarm() {
-            if (DEBUG)
+            if (DEBUG) {
                 Slog.d(TAG, "Time to trigger periodic alarm.");
+            }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
                     Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
@@ -460,7 +466,7 @@
                     return;
                 }
                 try {
-                  sStatsd.informDeviceShutdown();
+                    sStatsd.informDeviceShutdown();
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
                 }
@@ -499,9 +505,11 @@
     @Override // Binder call
     public void setAlarmForSubscriberTriggering(long timestampMs) {
         enforceCallingPermission();
-        if (DEBUG)
-            Slog.d(TAG, "Setting periodic alarm in about " +
-                    (timestampMs - SystemClock.elapsedRealtime()));
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "Setting periodic alarm in about " + (timestampMs
+                            - SystemClock.elapsedRealtime()));
+        }
         final long callingToken = Binder.clearCallingIdentity();
         try {
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
@@ -516,8 +524,9 @@
     @Override // Binder call
     public void cancelAlarmForSubscriberTriggering() {
         enforceCallingPermission();
-        if (DEBUG)
+        if (DEBUG) {
             Slog.d(TAG, "Cancelling periodic alarm");
+        }
         final long callingToken = Binder.clearCallingIdentity();
         try {
             mAlarmManager.cancel(mPeriodicAlarmListener);
@@ -547,8 +556,9 @@
     @Override // Binder call
     public void cancelPullingAlarm() {
         enforceCallingPermission();
-        if (DEBUG)
+        if (DEBUG) {
             Slog.d(TAG, "Cancelling pulling alarm");
+        }
         final long callingToken = Binder.clearCallingIdentity();
         try {
             mAlarmManager.cancel(mPullingAlarmListener);
@@ -561,10 +571,11 @@
             int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
         int size = stats.size();
         long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
         NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
         for (int j = 0; j < size; j++) {
             stats.getValues(j, entry);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
             e.writeInt(entry.uid);
             if (withFGBG) {
                 e.writeInt(entry.set);
@@ -640,14 +651,15 @@
         return null;
     }
 
-    private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullKernelWakelock(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         final KernelWakelockStats wakelockStats =
                 mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
             String name = ent.getKey();
             KernelWakelockStats.Entry kws = ent.getValue();
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeString(name);
             e.writeInt(kws.mCount);
             e.writeInt(kws.mVersion);
@@ -656,7 +668,9 @@
         }
     }
 
-    private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiBytesTransfer(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             // TODO: Consider caching the following call to get BatteryStatsInternal.
@@ -668,7 +682,8 @@
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             // Combine all the metrics per Uid into one record.
             NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null)
                             .groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
         } catch (java.io.IOException e) {
@@ -678,7 +693,9 @@
         }
     }
 
-    private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiBytesTransferByFgBg(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -688,7 +705,8 @@
             }
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null));
             addNetworkStats(tagId, pulledData, stats, true);
         } catch (java.io.IOException e) {
             Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
@@ -697,7 +715,9 @@
         }
     }
 
-    private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullMobileBytesTransfer(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -708,7 +728,8 @@
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             // Combine all the metrics per Uid into one record.
             NetworkStats stats =
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null)
                             .groupedByUid();
             addNetworkStats(tagId, pulledData, stats, false);
         } catch (java.io.IOException e) {
@@ -718,12 +739,14 @@
         }
     }
 
-    private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBluetoothBytesTransfer(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BluetoothActivityEnergyInfo info = pullBluetoothData();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         if (info.getUidTraffic() != null) {
             for (UidTraffic traffic : info.getUidTraffic()) {
-                StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
                 e.writeInt(traffic.getUid());
                 e.writeLong(traffic.getRxBytes());
                 e.writeLong(traffic.getTxBytes());
@@ -732,7 +755,9 @@
         }
     }
 
-    private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullMobileBytesTransferByFgBg(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
             BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -742,7 +767,8 @@
             }
             NetworkStatsFactory nsf = new NetworkStatsFactory();
             NetworkStats stats = rollupNetworkStatsByFGBG(
-                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+                    nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE,
+                            null));
             addNetworkStats(tagId, pulledData, stats, true);
         } catch (java.io.IOException e) {
             Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
@@ -751,13 +777,15 @@
         }
     }
 
-    private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullCpuTimePerFreq(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
             long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
             if (clusterTimeMs != null) {
                 for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                            wallClockNanos);
                     e.writeInt(cluster);
                     e.writeInt(speed);
                     e.writeLong(clusterTimeMs[speed]);
@@ -767,10 +795,11 @@
         }
     }
 
-    private void pullKernelUidCpuTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuTimeReader.readAbsolute((uid, userTimeUs, systemTimeUs) -> {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(uid);
             e.writeLong(userTimeUs);
             e.writeLong(systemTimeUs);
@@ -778,12 +807,14 @@
         });
     }
 
-    private void pullKernelUidCpuFreqTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuFreqTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
             for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
-                if(cpuFreqTimeMs[freqIndex] != 0) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                if (cpuFreqTimeMs[freqIndex] != 0) {
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                            wallClockNanos);
                     e.writeInt(uid);
                     e.writeInt(freqIndex);
                     e.writeLong(cpuFreqTimeMs[freqIndex]);
@@ -793,11 +824,13 @@
         });
     }
 
-    private void pullKernelUidCpuClusterTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuClusterTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
             for (int i = 0; i < cpuClusterTimesMs.length; i++) {
-                StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
                 e.writeInt(uid);
                 e.writeInt(i);
                 e.writeLong(cpuClusterTimesMs[i]);
@@ -806,17 +839,20 @@
         });
     }
 
-    private void pullKernelUidCpuActiveTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullKernelUidCpuActiveTime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         mKernelUidCpuActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(uid);
-            e.writeLong((long)cpuActiveTimesMs);
+            e.writeLong((long) cpuActiveTimesMs);
             pulledData.add(e);
         });
     }
 
-    private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiActivityInfo(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         if (mWifiManager == null) {
             mWifiManager =
@@ -827,7 +863,8 @@
                 SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
                 mWifiManager.requestActivityInfo(wifiReceiver);
                 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
-                StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
                 e.writeLong(wifiInfo.getTimeStamp());
                 e.writeInt(wifiInfo.getStackState());
                 e.writeLong(wifiInfo.getControllerTxTimeMillis());
@@ -836,14 +873,18 @@
                 e.writeLong(wifiInfo.getControllerEnergyUsed());
                 pulledData.add(e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+                Slog.e(TAG,
+                        "Pulling wifiManager for wifi controller activity energy info has error",
+                        e);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
     }
 
-    private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullModemActivityInfo(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         if (mTelephony == null) {
             mTelephony = TelephonyManager.from(mContext);
@@ -852,7 +893,7 @@
             SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
             mTelephony.requestModemActivityInfo(modemReceiver);
             final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 10);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeLong(modemInfo.getTimestamp());
             e.writeLong(modemInfo.getSleepTimeMillis());
             e.writeLong(modemInfo.getIdleTimeMillis());
@@ -867,9 +908,11 @@
         }
     }
 
-    private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBluetoothActivityInfo(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BluetoothActivityEnergyInfo info = pullBluetoothData();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(info.getTimeStamp());
         e.writeInt(info.getBluetoothStackState());
         e.writeLong(info.getControllerTxTimeMillis());
@@ -882,7 +925,8 @@
     private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null) {
-            SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
+            SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
+                    "bluetooth");
             adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
             return awaitControllerInfo(bluetoothReceiver);
         } else {
@@ -891,25 +935,29 @@
         }
     }
 
-    private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+    private void pullSystemElapsedRealtime(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(SystemClock.elapsedRealtime());
         pulledData.add(e);
     }
 
-    private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
+    private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(SystemClock.uptimeMillis());
         pulledData.add(e);
     }
 
-    private void pullProcessMemoryState(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullProcessMemoryState(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         List<ProcessMemoryState> processMemoryStates =
-                LocalServices.getService(ActivityManagerInternal.class)
-                        .getMemoryStateForProcesses();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+                LocalServices.getService(
+                        ActivityManagerInternal.class).getMemoryStateForProcesses();
         for (ProcessMemoryState processMemoryState : processMemoryStates) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(processMemoryState.uid);
             e.writeString(processMemoryState.processName);
             e.writeInt(processMemoryState.oomScore);
@@ -922,7 +970,9 @@
         }
     }
 
-    private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBinderCallsStats(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BinderCallsStatsService.Internal binderStats =
                 LocalServices.getService(BinderCallsStatsService.Internal.class);
         if (binderStats == null) {
@@ -931,9 +981,8 @@
 
         List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
         binderStats.reset();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (ExportedCallStat callStat : callStats) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 13 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(callStat.uid);
             e.writeString(callStat.className);
             e.writeString(callStat.methodName);
@@ -951,7 +1000,9 @@
         }
     }
 
-    private void pullBinderCallsStatsExceptions(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullBinderCallsStatsExceptions(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         BinderCallsStatsService.Internal binderStats =
                 LocalServices.getService(BinderCallsStatsService.Internal.class);
         if (binderStats == null) {
@@ -961,16 +1012,16 @@
         ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
         // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
         // can reset the exception stats.
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeString(entry.getKey());
             e.writeInt(entry.getValue());
             pulledData.add(e);
         }
     }
 
-    private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         LooperStats looperStats = LocalServices.getService(LooperStats.class);
         if (looperStats == null) {
             return;
@@ -978,10 +1029,9 @@
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
         looperStats.reset();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (LooperStats.ExportedEntry entry : entries) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 10 /* fields */);
-            e.writeInt(1000); // uid collection not implemented yet
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(entry.workSourceUid);
             e.writeString(entry.handlerClassName);
             e.writeString(entry.threadName);
             e.writeString(entry.messageName);
@@ -995,7 +1045,8 @@
         }
     }
 
-    private void pullDiskStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         // Run a quick-and-dirty performance test: write 512 bytes
         byte[] junk = new byte[512];
         for (int i = 0; i < junk.length; i++) junk[i] = (byte) i;  // Write nonzero bytes
@@ -1042,41 +1093,40 @@
         }
 
         // Add info pulledData.
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(latency);
         e.writeBoolean(fileBased);
         e.writeInt(writeSpeed);
         pulledData.add(e);
     }
 
-    private void pullDirectoryUsage(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
         StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
         StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
 
-        StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
         e.writeLong(statFsData.getAvailableBytes());
         e.writeLong(statFsData.getTotalBytes());
         pulledData.add(e);
 
-        e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
         e.writeLong(statFsCache.getAvailableBytes());
         e.writeLong(statFsCache.getTotalBytes());
         pulledData.add(e);
 
-        e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+        e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
         e.writeLong(statFsSystem.getAvailableBytes());
         e.writeLong(statFsSystem.getTotalBytes());
         pulledData.add(e);
     }
 
-    private void pullAppSize(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         try {
             String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
             JSONObject json = new JSONObject(jsonStr);
@@ -1094,7 +1144,7 @@
             }
             for (int i = 0; i < length; i++) {
                 StatsLogEventWrapper e =
-                        new StatsLogEventWrapper(elapsedNanos, tagId, 5 /* fields */);
+                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
                 e.writeString(pkg_names.getString(i));
                 e.writeLong(app_sizes.optLong(i, -1L));
                 e.writeLong(app_data_sizes.optLong(i, -1L));
@@ -1107,62 +1157,62 @@
         }
     }
 
-    private void pullCategorySize(int tagId, List<StatsLogEventWrapper> pulledData) {
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+    private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         try {
             String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
             JSONObject json = new JSONObject(jsonStr);
             long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
 
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
             e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
             e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
             e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
             e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
             e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
             e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
             e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
             e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
             e.writeLong(cacheTime);
             pulledData.add(e);
 
-            e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+            e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
             e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
             e.writeLong(cacheTime);
@@ -1172,7 +1222,8 @@
         }
     }
 
-    private void pullNumFingerprints(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullNumFingerprints(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
         FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class);
         if (fingerprintManager == null) {
             return;
@@ -1182,11 +1233,10 @@
             return;
         }
         final long token = Binder.clearCallingIdentity();
-        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (UserInfo user : userManager.getUsers()) {
             final int userId = user.getUserHandle().getIdentifier();
             final int numFingerprints = fingerprintManager.getEnrolledFingerprints(userId).size();
-            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 2 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(userId);
             e.writeInt(numFingerprints);
             pulledData.add(e);
@@ -1200,108 +1250,111 @@
     @Override // Binder call
     public StatsLogEventWrapper[] pullData(int tagId) {
         enforceCallingPermission();
-        if (DEBUG)
+        if (DEBUG) {
             Slog.d(TAG, "Pulling " + tagId);
+        }
         List<StatsLogEventWrapper> ret = new ArrayList<>();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+        long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
         switch (tagId) {
             case StatsLog.WIFI_BYTES_TRANSFER: {
-                pullWifiBytesTransfer(tagId, ret);
+                pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.MOBILE_BYTES_TRANSFER: {
-                pullMobileBytesTransfer(tagId, ret);
+                pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
-                pullWifiBytesTransferByFgBg(tagId, ret);
+                pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
-                pullMobileBytesTransferByFgBg(tagId, ret);
+                pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
-                pullBluetoothBytesTransfer(tagId, ret);
+                pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.KERNEL_WAKELOCK: {
-                pullKernelWakelock(tagId, ret);
+                pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_TIME_PER_FREQ: {
-                pullCpuTimePerFreq(tagId, ret);
+                pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_TIME_PER_UID: {
-                pullKernelUidCpuTime(tagId, ret);
+                pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_TIME_PER_UID_FREQ: {
-                pullKernelUidCpuFreqTime(tagId, ret);
+                pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_CLUSTER_TIME: {
-                pullKernelUidCpuClusterTime(tagId, ret);
+                pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CPU_ACTIVE_TIME: {
-                pullKernelUidCpuActiveTime(tagId, ret);
+                pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.WIFI_ACTIVITY_INFO: {
-                pullWifiActivityInfo(tagId, ret);
+                pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.MODEM_ACTIVITY_INFO: {
-                pullModemActivityInfo(tagId, ret);
+                pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
-                pullBluetoothActivityInfo(tagId, ret);
+                pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.SYSTEM_UPTIME: {
-                pullSystemUpTime(tagId, ret);
+                pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.SYSTEM_ELAPSED_REALTIME: {
-                pullSystemElapsedRealtime(tagId, ret);
+                pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.PROCESS_MEMORY_STATE: {
-                pullProcessMemoryState(tagId, ret);
+                pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BINDER_CALLS: {
-                pullBinderCallsStats(tagId, ret);
+                pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.BINDER_CALLS_EXCEPTIONS: {
-                pullBinderCallsStatsExceptions(tagId, ret);
+                pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.LOOPER_STATS: {
-                pullLooperStats(tagId, ret);
+                pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.DISK_STATS: {
-                pullDiskStats(tagId, ret);
+                pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.DIRECTORY_USAGE: {
-                pullDirectoryUsage(tagId, ret);
+                pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.APP_SIZE: {
-                pullAppSize(tagId, ret);
+                pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.CATEGORY_SIZE: {
-                pullCategorySize(tagId, ret);
+                pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             case StatsLog.NUM_FINGERPRINTS: {
-                pullNumFingerprints(tagId, ret);
+                pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
             default:
@@ -1513,7 +1566,8 @@
 
     // Thermal event received from vendor thermal management subsystem
     private static final class ThermalEventListener extends IThermalEventListener.Stub {
-        @Override public void notifyThrottling(boolean isThrottling, Temperature temp) {
+        @Override
+        public void notifyThrottling(boolean isThrottling, Temperature temp) {
             StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(),
                     isThrottling ? 1 : 0, temp.getValue());
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6c7304d..6aca464 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -150,7 +150,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
-import com.android.internal.view.IInputMethodClient;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.RotationCache;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -572,7 +571,7 @@
             if (!w.mLayoutAttached) {
                 if (mTmpInitial) {
                     //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                    w.mContentChanged = false;
+                    w.resetContentChanged();
                 }
                 if (w.mAttrs.type == TYPE_DREAM) {
                     // Don't layout windows behind a dream, so that if it does stuff like hide
@@ -617,7 +616,7 @@
                     || w.mLayoutNeeded) {
                 if (mTmpInitial) {
                     //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
-                    w.mContentChanged = false;
+                    w.resetContentChanged();
                 }
                 w.mLayoutNeeded = false;
                 w.prelayout();
@@ -700,7 +699,7 @@
         final WindowStateAnimator winAnimator = w.mWinAnimator;
 
         //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
-        w.mContentChanged = false;
+        w.resetContentChanged();
 
         // Moved from updateWindowsAndWallpaperLocked().
         if (w.mHasSurface) {
@@ -2949,7 +2948,7 @@
         }
     }
 
-    boolean inputMethodClientHasFocus(IInputMethodClient client) {
+    boolean isInputMethodClientFocus(int uid, int pid) {
         final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
         if (imFocus == null) {
             return false;
@@ -2961,17 +2960,13 @@
             Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
         }
 
-        final IInputMethodClient imeClient = imFocus.mSession.mClient;
-
         if (DEBUG_INPUT_METHOD) {
-            Slog.i(TAG_WM, "IM target client: " + imeClient);
-            if (imeClient != null) {
-                Slog.i(TAG_WM, "IM target client binder: " + imeClient.asBinder());
-                Slog.i(TAG_WM, "Requesting client binder: " + client.asBinder());
-            }
+            Slog.i(TAG_WM, "IM target uid/pid: " + imFocus.mSession.mUid
+                    + "/" + imFocus.mSession.mPid);
+            Slog.i(TAG_WM, "Requesting client uid/pid: " + uid + "/" + pid);
         }
 
-        return imeClient != null && imeClient.asBinder() == client.asBinder();
+        return imFocus.mSession.mUid == uid && imFocus.mSession.mPid == pid;
     }
 
     boolean hasSecureWindowOnScreen() {
@@ -3256,7 +3251,7 @@
         // TODO(b/68392460): We should screenshot Task controls directly
         // but it's difficult at the moment as the Task doesn't have the
         // correct size set.
-        final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot);
+        final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, inRotation, rot);
         if (bitmap == null) {
             Slog.w(TAG_WM, "Failed to take screenshot");
             return null;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index f9a71d3..acc9c03 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.MergedConfiguration;
@@ -56,10 +55,6 @@
 import android.view.WindowManager;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
@@ -73,8 +68,6 @@
 class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
     final WindowManagerService mService;
     final IWindowSessionCallback mCallback;
-    final IInputMethodClient mClient;
-    final InputMethodManagerInternal mInputMethodManagerInternal;
     final int mUid;
     final int mPid;
     private final String mStringName;
@@ -95,17 +88,9 @@
     private String mPackageName;
     private String mRelayoutTag;
 
-    public Session(WindowManagerService service, IWindowSessionCallback callback,
-            IInputMethodClient client, IInputContext inputContext) {
+    public Session(WindowManagerService service, IWindowSessionCallback callback) {
         mService = service;
         mCallback = callback;
-        mClient = client;
-        // Depending on the timing when Session object gets called and SystemServer#mFactoryTestMode
-        // this could be null, right?
-        final InputMethodManagerInternal immInternal =
-                LocalServices.getService(InputMethodManagerInternal.class);
-        mInputMethodManagerInternal =
-                immInternal != null ? immInternal : InputMethodManagerInternal.NOP;
         mUid = Binder.getCallingUid();
         mPid = Binder.getCallingPid();
         mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
@@ -134,12 +119,11 @@
         sb.append("}");
         mStringName = sb.toString();
 
-        mInputMethodManagerInternal.addClient(client, inputContext, mUid, mPid);
         try {
-            client.asBinder().linkToDeath(this, 0);
+            mCallback.asBinder().linkToDeath(this, 0);
         } catch (RemoteException e) {
             // The caller has died, so we can just forget about this.
-            mInputMethodManagerInternal.removeClient(client);
+            // Hmmm, should we call killSessionLocked()??
         }
     }
 
@@ -159,9 +143,8 @@
 
     @Override
     public void binderDied() {
-        mInputMethodManagerInternal.removeClient(mClient);
         synchronized(mService.mWindowMap) {
-            mClient.asBinder().unlinkToDeath(this, 0);
+            mCallback.asBinder().unlinkToDeath(this, 0);
             mClientDead = true;
             killSessionLocked();
         }
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index c99329a..0c9a14b 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -21,7 +21,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     },
@@ -35,7 +35,7 @@
           "include-annotation": "android.platform.test.annotations.Presubmit"
         },
         {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     }
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index cbe7d9d..20a874b 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -36,7 +36,6 @@
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayCutout;
-import android.view.WindowManager;
 
 import com.android.server.wm.utils.InsetUtils;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -191,6 +190,10 @@
 
     private final Rect mTmpRect = new Rect();
 
+    private boolean mHasOutsets;
+
+    private boolean mContentChanged;
+
     public WindowFrames() {
     }
 
@@ -237,11 +240,9 @@
      * Calculates the outsets for this windowFrame. The outsets are calculated by the area between
      * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
      * {@link #mOutsets} is set to empty.
-     *
-     * @param hasOutsets Whether this frame has outsets.
      */
-    void calculateOutsets(boolean hasOutsets) {
-        if (hasOutsets) {
+    void calculateOutsets() {
+        if (mHasOutsets) {
             InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
         } else {
             mOutsets.setEmpty();
@@ -249,7 +250,8 @@
     }
 
     /**
-     * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+     * Calculate the insets for the type
+     * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
      *
      * @param cutoutInsets The insets for the cutout.
      */
@@ -367,6 +369,28 @@
         mLastContentInsets.set(-1, -1, -1, -1);
     }
 
+    /**
+     * Sets whether the frame has outsets.
+     */
+    public void setHasOutsets(boolean hasOutsets) {
+        mHasOutsets = hasOutsets;
+    }
+
+    /**
+     * Sets whether the content has changed. This means that either the size or parent frame has
+     * changed.
+     */
+    public void setContentChanged(boolean contentChanged) {
+        mContentChanged = contentChanged;
+    }
+
+    /**
+     * @see #setContentChanged(boolean)
+     */
+    boolean hasContentChanged() {
+        return mContentChanged;
+    }
+
     public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         mParentFrame.writeToProto(proto, PARENT_FRAME);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 793ce60..57cae39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,7 +30,6 @@
 import android.view.MagnificationSpec;
 import android.view.WindowInfo;
 
-import com.android.internal.view.IInputMethodClient;
 import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -444,9 +443,14 @@
     public abstract boolean isUidFocused(int uid);
 
     /**
-     * Returns {@code true} if a process that is identified by {@code client} has IME focus.
+     * Checks whether the specified process has IME focus or not.
+     *
+     * @param uid UID of the process to be queried
+     * @param pid PID of the process to be queried
+     * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
+     *         focus
      */
-    public abstract boolean inputMethodClientHasFocus(IInputMethodClient client);
+    public abstract boolean isInputMethodClientFocus(int uid, int pid);
 
     /**
      * Return the display Id for given window.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2ed09ae..71ce1d9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -232,8 +232,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.WindowManagerPolicyThread;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
@@ -5037,12 +5035,8 @@
     // -------------------------------------------------------------
 
     @Override
-    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
-            IInputContext inputContext) {
-        if (client == null) throw new IllegalArgumentException("null client");
-        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
-        Session session = new Session(this, callback, client, inputContext);
-        return session;
+    public IWindowSession openSession(IWindowSessionCallback callback) {
+        return new Session(this, callback);
     }
 
     @Override
@@ -5075,9 +5069,7 @@
             throw new SecurityException("Must hold permission " +
                     android.Manifest.permission.WRITE_SECURE_SETTINGS);
         }
-        if (displayId != DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can only set the default display");
-        }
+
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
@@ -5110,9 +5102,7 @@
             throw new SecurityException("Must hold permission " +
                     android.Manifest.permission.WRITE_SECURE_SETTINGS);
         }
-        if (displayId != DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can only set the default display");
-        }
+
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
@@ -5192,9 +5182,7 @@
             throw new SecurityException("Must hold permission " +
                     android.Manifest.permission.WRITE_SECURE_SETTINGS);
         }
-        if (displayId != DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can only set the default display");
-        }
+
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized(mWindowMap) {
@@ -5241,9 +5229,6 @@
             throw new SecurityException("Must hold permission " +
                     android.Manifest.permission.WRITE_SECURE_SETTINGS);
         }
-        if (displayId != DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can only set the default display");
-        }
 
         final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser",
@@ -5272,9 +5257,6 @@
             throw new SecurityException("Must hold permission " +
                     android.Manifest.permission.WRITE_SECURE_SETTINGS);
         }
-        if (displayId != DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can only set the default display");
-        }
 
         final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser",
@@ -7411,32 +7393,32 @@
             }
         }
 
-    public boolean inputMethodClientHasFocus(IInputMethodClient client) {
-        boolean hasFocus;
-        synchronized (mWindowMap) {
-            // Check all displays if any input method window has focus.
-            for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
-                final DisplayContent displayContent = mRoot.mChildren.get(i);
-                if (displayContent.inputMethodClientHasFocus(client)) {
+        @Override
+        public boolean isInputMethodClientFocus(int uid, int pid) {
+            synchronized (mWindowMap) {
+                // Check all displays if any input method window has focus.
+                for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
+                    final DisplayContent displayContent = mRoot.mChildren.get(i);
+                    if (displayContent.isInputMethodClientFocus(uid, pid)) {
+                        return true;
+                    }
+                }
+
+                // Okay, how about this...  what is the current focus?
+                // It seems in some cases we may not have moved the IM
+                // target window, such as when it was in a pop-up window,
+                // so let's also look at the current focus.  (An example:
+                // go to Gmail, start searching so the keyboard goes up,
+                // press home.  Sometimes the IME won't go down.)
+                // Would be nice to fix this more correctly, but it's
+                // way at the end of a release, and this should be good enough.
+                if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
+                        && mCurrentFocus.mSession.mPid == pid) {
                     return true;
                 }
             }
-
-            // Okay, how about this...  what is the current focus?
-            // It seems in some cases we may not have moved the IM
-            // target window, such as when it was in a pop-up window,
-            // so let's also look at the current focus.  (An example:
-            // go to Gmail, start searching so the keyboard goes up,
-            // press home.  Sometimes the IME won't go down.)
-            // Would be nice to fix this more correctly, but it's
-            // way at the end of a release, and this should be good enough.
-            if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
-                    && mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
-                return true;
-            }
+            return false;
         }
-        return false;
-    }
 
         @Override
         public int getDisplayIdForWindow(IBinder windowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 6e0ccfd..831418b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -20,7 +20,6 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.os.UserHandle;
@@ -28,10 +27,6 @@
 import android.view.Display;
 import android.view.IWindowManager;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -87,21 +82,48 @@
         return -1;
     }
 
+    private int getDisplayId(String opt) {
+        int displayId = Display.DEFAULT_DISPLAY;
+        String option = "-d".equals(opt) ? opt : getNextOption();
+        if (option != null && "-d".equals(option)) {
+            try {
+                displayId = Integer.parseInt(getNextArgRequired());
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: bad number " + e);
+            } catch (IllegalArgumentException e) {
+                getErrPrintWriter().println("Error: " + e);
+            }
+        }
+        return displayId;
+    }
+
+    private void printInitialDisplaySize(PrintWriter pw , int displayId) {
+        final Point initialSize = new Point();
+        final Point baseSize = new Point();
+
+        try {
+            mInterface.getInitialDisplaySize(displayId, initialSize);
+            mInterface.getBaseDisplaySize(displayId, baseSize);
+            pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+            if (!initialSize.equals(baseSize)) {
+                pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+            }
+        } catch (RemoteException e) {
+            // Can't call getInitialDisplaySize() on IWindowManager or
+            // Can't call getBaseDisplaySize() on IWindowManager
+            pw.println("Remote exception: " + e);
+        }
+    }
+
     private int runDisplaySize(PrintWriter pw) throws RemoteException {
         String size = getNextArg();
         int w, h;
+        final int displayId = getDisplayId(size);
         if (size == null) {
-            Point initialSize = new Point();
-            Point baseSize = new Point();
-            try {
-                mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
-                mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
-                pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
-                if (!initialSize.equals(baseSize)) {
-                    pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
-                }
-            } catch (RemoteException e) {
-            }
+            printInitialDisplaySize(pw, displayId);
+            return 0;
+        } else if ("-d".equals(size)) {
+            printInitialDisplaySize(pw, displayId);
             return 0;
         } else if ("reset".equals(size)) {
             w = h = -1;
@@ -114,8 +136,8 @@
             String wstr = size.substring(0, div);
             String hstr = size.substring(div+1);
             try {
-                w = parseDimension(wstr);
-                h = parseDimension(hstr);
+                w = parseDimension(wstr, displayId);
+                h = parseDimension(hstr, displayId);
             } catch (NumberFormatException e) {
                 getErrPrintWriter().println("Error: bad number " + e);
                 return -1;
@@ -123,27 +145,38 @@
         }
 
         if (w >= 0 && h >= 0) {
-            // TODO(multidisplay): For now Configuration only applies to main screen.
-            mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+            mInterface.setForcedDisplaySize(displayId, w, h);
         } else {
-            mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+            mInterface.clearForcedDisplaySize(displayId);
         }
         return 0;
     }
 
+    private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
+        try {
+            final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
+            final int baseDensity = mInterface.getBaseDisplayDensity(displayId);
+            pw.println("Physical density: " + initialDensity);
+            if (initialDensity != baseDensity) {
+                pw.println("Override density: " + baseDensity);
+            }
+        } catch (RemoteException e) {
+            // Can't call getInitialDisplayDensity() on IWindowManager or
+            // Can't call getBaseDisplayDensity() on IWindowManager
+            pw.println("Remote exception: " + e);
+        }
+    }
+
     private int runDisplayDensity(PrintWriter pw) throws RemoteException {
         String densityStr = getNextArg();
         int density;
+        final int displayId = getDisplayId(densityStr);
+
         if (densityStr == null) {
-            try {
-                int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
-                int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
-                pw.println("Physical density: " + initialDensity);
-                if (initialDensity != baseDensity) {
-                    pw.println("Override density: " + baseDensity);
-                }
-            } catch (RemoteException e) {
-            }
+            printInitialDisplayDensity(pw, displayId);
+            return 0;
+        } else if ("-d".equals(densityStr)) {
+            printInitialDisplayDensity(pw, displayId);
             return 0;
         } else if ("reset".equals(densityStr)) {
             density = -1;
@@ -161,11 +194,10 @@
         }
 
         if (density > 0) {
-            // TODO(multidisplay): For now Configuration only applies to main screen.
-            mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+            mInterface.setForcedDisplayDensityForUser(displayId, density,
                     UserHandle.USER_CURRENT);
         } else {
-            mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+            mInterface.clearForcedDisplayDensityForUser(displayId,
                     UserHandle.USER_CURRENT);
         }
         return 0;
@@ -174,6 +206,7 @@
     private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
         String overscanStr = getNextArgRequired();
         Rect rect = new Rect();
+        final int displayId = getDisplayId(overscanStr);
         if ("reset".equals(overscanStr)) {
             rect.set(0, 0, 0, 0);
         } else {
@@ -190,17 +223,16 @@
             rect.bottom = Integer.parseInt(matcher.group(4));
         }
 
-        mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
-                rect.bottom);
+        mInterface.setOverscan(displayId, rect.left, rect.top, rect.right, rect.bottom);
         return 0;
     }
 
     private int runDisplayScaling(PrintWriter pw) throws RemoteException {
         String scalingStr = getNextArgRequired();
         if ("auto".equals(scalingStr)) {
-            mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0);
         } else if ("off".equals(scalingStr)) {
-            mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+            mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1);
         } else {
             getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
             return -1;
@@ -213,14 +245,14 @@
         return 0;
     }
 
-    private int parseDimension(String s) throws NumberFormatException {
+    private int parseDimension(String s, int displayId) throws NumberFormatException {
         if (s.endsWith("px")) {
             return Integer.parseInt(s.substring(0, s.length() - 2));
         }
         if (s.endsWith("dp")) {
             int density;
             try {
-                density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+                density = mInterface.getBaseDisplayDensity(displayId);
             } catch (RemoteException e) {
                 density = DisplayMetrics.DENSITY_DEFAULT;
             }
@@ -236,14 +268,14 @@
         pw.println("Window manager (window) commands:");
         pw.println("  help");
         pw.println("      Print this help text.");
-        pw.println("  size [reset|WxH|WdpxHdp]");
+        pw.println("  size [reset|WxH|WdpxHdp] [-d DISPLAY_ID]");
         pw.println("    Return or override display size.");
         pw.println("    width and height in pixels unless suffixed with 'dp'.");
-        pw.println("  density [reset|DENSITY]");
+        pw.println("  density [reset|DENSITY] [-d DISPLAY_ID]");
         pw.println("    Return or override display density.");
-        pw.println("  overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+        pw.println("  overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]");
         pw.println("    Set overscan area for display.");
-        pw.println("  scaling [off|auto]");
+        pw.println("  scaling [off|auto] [-d DISPLAY_ID]");
         pw.println("    Set display scaling mode.");
         pw.println("  dismiss-keyguard");
         pw.println("    Dismiss the keyguard, prompting user for auth if necessary.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5272b66..f1ddda7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -359,8 +359,6 @@
      */
     private final Rect mInsetFrame = new Rect();
 
-    boolean mContentChanged;
-
     // If a window showing a wallpaper: the requested offset for the
     // wallpaper; if a wallpaper window: the currently applied offset.
     float mWallpaperX = -1;
@@ -787,7 +785,7 @@
     }
 
     @Override
-    public void computeFrameLw(WindowFrames windowFrames) {
+    public void computeFrameLw() {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -796,8 +794,6 @@
             return;
         }
         mHaveFrame = true;
-        mWindowFrames.setParentFrameWasClippedByDisplayCutout(
-                windowFrames.parentFrameWasClippedByDisplayCutout());
 
         final Task task = getTask();
         final boolean inFullscreenContainer = inFullscreenContainer();
@@ -827,10 +823,9 @@
         final int layoutYDiff;
         if (inFullscreenContainer || layoutInParentFrame()) {
             // We use the parent frame as the containing frame for fullscreen and child windows
-            mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
-            mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
-            layoutDisplayFrame = windowFrames.mDisplayFrame;
-            layoutContainingFrame = windowFrames.mParentFrame;
+            mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
+            layoutDisplayFrame = mWindowFrames.mDisplayFrame;
+            layoutContainingFrame = mWindowFrames.mParentFrame;
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
@@ -849,17 +844,17 @@
             // IME is up and obscuring this window. Adjust the window position so it is visible.
             if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
                 if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
-                        > windowFrames.mContentFrame.bottom) {
+                        > mWindowFrames.mContentFrame.bottom) {
                     // In freeform we want to move the top up directly.
                     // TODO: Investigate why this is contentFrame not parentFrame.
                     mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
-                            - windowFrames.mContentFrame.bottom;
+                            - mWindowFrames.mContentFrame.bottom;
                 } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
-                        > windowFrames.mParentFrame.bottom) {
+                        > mWindowFrames.mParentFrame.bottom) {
                     // But in docked we want to behave like fullscreen and behave as if the task
                     // were given smaller bounds for the purposes of layout. Skip adjustments for
                     // the pinned stack, they are handled separately in the PinnedStackController.
-                    mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
+                    mWindowFrames.mContainingFrame.bottom = mWindowFrames.mParentFrame.bottom;
                 }
             }
 
@@ -868,7 +863,7 @@
                 // if it wasn't set already. No need to intersect it with the (visible)
                 // "content frame" since it is allowed to be outside the visible desktop.
                 if (mWindowFrames.mContainingFrame.isEmpty()) {
-                    mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
+                    mWindowFrames.mContainingFrame.set(mWindowFrames.mContentFrame);
                 }
             }
 
@@ -878,10 +873,11 @@
                 // PIP edge case: When going from pinned to fullscreen, we apply a
                 // tempInsetFrame for the full task - but we're still at the start of the animation.
                 // To prevent a jump if there's a letterbox, restrict to the parent frame.
-                mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
-                mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
+                mInsetFrame.intersectUnchecked(mWindowFrames.mParentFrame);
+                mWindowFrames.mContainingFrame.intersectUnchecked(mWindowFrames.mParentFrame);
             }
 
+            layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
             mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
             layoutXDiff =
                     !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
@@ -892,41 +888,24 @@
             layoutContainingFrame =
                     !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
             mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
-            subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
-                    windowFrames.mDisplayFrame, mTmpRect);
+            subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
+                    mTmpRect);
             if (!layoutInParentFrame()) {
                 subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
-                        windowFrames.mParentFrame, mTmpRect);
-                subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+                        mWindowFrames.mParentFrame, mTmpRect);
+                subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame,
                         mTmpRect);
             }
-            layoutDisplayFrame = windowFrames.mDisplayFrame;
             layoutDisplayFrame.intersect(layoutContainingFrame);
         }
 
         final int pw = mWindowFrames.mContainingFrame.width();
         final int ph = mWindowFrames.mContainingFrame.height();
 
-        if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
-            //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
-            //        + " to " + parentFrame);
-            mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
-            mContentChanged = true;
-        }
         if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
             mLastRequestedWidth = mRequestedWidth;
             mLastRequestedHeight = mRequestedHeight;
-            mContentChanged = true;
-        }
-
-        mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
-        mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
-        mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
-        mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
-        mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
-        final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
-        if (hasOutsets) {
-            mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
+            mWindowFrames.setContentChanged(true);
         }
 
         final int fw = mWindowFrames.mFrame.width();
@@ -935,7 +914,7 @@
         applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
 
         // Calculate the outsets before the content frame gets shrinked to the window frame.
-        mWindowFrames.calculateOutsets(hasOutsets);
+        mWindowFrames.calculateOutsets();
 
         // Make sure the content and visible frames are inside of the
         // final window frame.
@@ -997,7 +976,7 @@
         }
 
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
-            final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
+            final WmDisplayCutout c = mWindowFrames.mDisplayCutout.calculateRelativeTo(
                     mWindowFrames.mDisplayFrame);
             mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
         } else {
@@ -1006,7 +985,7 @@
         }
 
         mWindowFrames.setDisplayCutout(
-                windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
+                mWindowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
 
         // Offset the actual frame by the amount layout frame is off.
         mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
@@ -1733,7 +1712,7 @@
      * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
      */
     private boolean hasMoved() {
-        return mHasSurface && (mContentChanged || mMovedByResize)
+        return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
                 && !mAnimatingExit
                 && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
                     || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
@@ -4762,6 +4741,15 @@
         return mWindowFrames.mVisibleInsets;
     }
 
+    @Override
+    public WindowFrames getWindowFrames() {
+        return mWindowFrames;
+    }
+
+    void resetContentChanged() {
+        mWindowFrames.setContentChanged(false);
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4c71d65..e76afa3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6019,7 +6019,7 @@
             success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
             if (!success) {
                 Slog.w(LOG_TAG, "Couldn't remove user " + userId);
-            } else if (isManagedProfile(userId)) {
+            } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) {
                 sendWipeProfileNotification(wipeReasonForUser);
             }
         } catch (RemoteException re) {
@@ -6034,7 +6034,6 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty");
         enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
 
         final ActiveAdmin admin;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index f6e5601..ffc7fa2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -188,17 +188,13 @@
 
     @Test
     public void testCanBeLaunchedOnDisplay() throws Exception {
-        testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
-                true /*activityResizeable*/, true /*expected*/);
+        mService.mSupportsMultiWindow = true;
+        final ActivityRecord activity = new ActivityBuilder(mService).build();
 
-        testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
-                false /*activityResizeable*/, false /*expected*/);
-
-        testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/,
-                true /*activityResizeable*/, false /*expected*/);
-
-        testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/,
-                false /*activityResizeable*/, true /*expected*/);
+        // An activity can be launched on default display.
+        assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY));
+        // An activity cannot be launched on a non-existent display.
+        assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1));
     }
 
     @Test
@@ -229,26 +225,4 @@
         assertNull(mActivity.pendingOptions);
         assertNotNull(activity2.pendingOptions);
     }
-
-    private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
-            boolean activityResizeable, boolean expected) {
-        mService.mSupportsMultiWindow = true;
-
-        final TaskRecord task = taskPresent
-                ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
-
-        if (task != null) {
-            task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
-        }
-
-        final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
-        record.info.resizeMode = activityResizeable
-                ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-
-        record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
-
-
-        verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
-                anyInt(), anyInt(), eq(record.info));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index b238e43..d34f951 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -30,7 +30,7 @@
 
 public class FakeWindowState implements WindowManagerPolicy.WindowState {
 
-    private WindowFrames windowFrames;
+    private WindowFrames mWindowFrames = new WindowFrames();
 
     public WindowManager.LayoutParams attrs;
     public int displayId;
@@ -53,41 +53,40 @@
     }
 
     @Override
-    public void computeFrameLw(WindowFrames windowFrames) {
-        this.windowFrames = windowFrames;
+    public void computeFrameLw() {
     }
 
     @Override
     public Rect getFrameLw() {
-        return windowFrames.mParentFrame;
+        return mWindowFrames.mParentFrame;
     }
 
     @Override
     public Rect getDisplayFrameLw() {
-        return windowFrames.mDisplayFrame;
+        return mWindowFrames.mDisplayFrame;
     }
 
     @Override
     public Rect getOverscanFrameLw() {
-        return windowFrames.mOverscanFrame;
+        return mWindowFrames.mOverscanFrame;
     }
 
     @Override
     public Rect getContentFrameLw() {
-        return windowFrames.mContentFrame;
+        return mWindowFrames.mContentFrame;
     }
 
     @Override
     public Rect getVisibleFrameLw() {
-        return windowFrames.mVisibleFrame;
+        return mWindowFrames.mVisibleFrame;
     }
 
     public Rect getStableFrame() {
-        return windowFrames.mStableFrame;
+        return mWindowFrames.mStableFrame;
     }
 
     public Rect getDecorFrame() {
-        return windowFrames.mDecorFrame;
+        return mWindowFrames.mDecorFrame;
     }
 
     @Override
@@ -252,6 +251,11 @@
     }
 
     @Override
+    public WindowFrames getWindowFrames() {
+        return mWindowFrames;
+    }
+
+    @Override
     public boolean isInputMethodTarget() {
         return false;
     }
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5039e42..8ac2930 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -21,18 +21,32 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerSaveState;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.PowerManagerService.Injector;
+import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.batterysaver.BatterySavingStats;
 
 import org.junit.Rule;
 import org.mockito.Mock;
@@ -47,11 +61,19 @@
     private static final boolean BATTERY_SAVER_ENABLED = true;
     private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
 
-    private @Mock BatterySaverPolicy mBatterySaverPolicy;
+    private @Mock BatterySaverPolicy mBatterySaverPolicyMock;
+    private @Mock LightsManager mLightsManagerMock;
+    private @Mock DisplayManagerInternal mDisplayManagerInternalMock;
+    private @Mock BatteryManagerInternal mBatteryManagerInternalMock;
+    private @Mock ActivityManagerInternal mActivityManagerInternalMock;
+    private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock;
+    private @Mock Notifier mNotifierMock;
     private PowerManagerService mService;
     private PowerSaveState mPowerSaveState;
     private DisplayPowerRequest mDisplayPowerRequest;
 
+
+
     @Rule
     public void setUp() throws Exception {
         super.setUp();
@@ -61,11 +83,51 @@
                 .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
                 .setBrightnessFactor(BRIGHTNESS_FACTOR)
                 .build();
-        when(mBatterySaverPolicy.getBatterySaverPolicy(
+        when(mBatterySaverPolicyMock.getBatterySaverPolicy(
                 eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS), anyBoolean()))
                 .thenReturn(mPowerSaveState);
+
         mDisplayPowerRequest = new DisplayPowerRequest();
-        mService = new PowerManagerService(getContext(), mBatterySaverPolicy);
+        addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+        addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+        addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+
+        mService = new PowerManagerService(getContext(), new Injector() {
+            Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                    SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
+                return mNotifierMock;
+            }
+
+            SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+                return mock(SuspendBlocker.class);
+            }
+
+            BatterySaverPolicy createBatterySaverPolicy(
+                    Object lock, Context context, BatterySavingStats batterySavingStats) {
+                return mBatterySaverPolicyMock;
+            }
+
+            NativeWrapper createNativeWrapper() {
+                return mNativeWrapperMock;
+            }
+        });
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(LightsManager.class);
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
     }
 
     @SmallTest
@@ -110,6 +172,25 @@
         mService.setVrModeEnabled(false);
         assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
                 DisplayPowerRequest.POLICY_BRIGHT);
+    }
 
+    @SmallTest
+    public void testWakefulnessAwake_InitialValue() throws Exception {
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+    }
+
+    @SmallTest
+    public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception {
+        // Start with AWAKE state
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        // Take a nap with a flag.
+        mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+            PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+
+        assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index e648230..0886729 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -202,8 +202,8 @@
         // When mFrame extends past cf, the content insets are
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
-        final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+        w.computeFrameLw();
         assertFrame(w, 0, 0, 1000, 1000);
         assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
         assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -217,7 +217,7 @@
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, 100, 100, 200, 200);
         assertContentInset(w, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
@@ -238,8 +238,8 @@
 
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
-        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
-        w.computeFrameLw(windowFrames);
+        w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw();
         assertFrame(w, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
@@ -247,14 +247,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertFrame(w, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertFrame(w, 0, 0, 300, 300);
@@ -267,14 +267,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -282,7 +282,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -292,16 +292,16 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
          assertFrame(w, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, 600, 600, 900, 900);
     }
 
@@ -322,8 +322,9 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        final WindowFrames windowFrames = w.getWindowFrames();
+        windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+        w.computeFrameLw();
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -336,7 +337,7 @@
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
@@ -354,7 +355,7 @@
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
@@ -384,13 +385,14 @@
         // We use a decor content frame with insets to produce cropping.
         Rect dcf = new Rect(cf);
 
-        final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        final WindowFrames windowFrames = w.getWindowFrames();
+        windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+        w.computeFrameLw();
         assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
 
         windowFrames.mDecorFrame.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
 
         // Now we set up a window which doesn't fill the entire decor frame.
@@ -404,7 +406,7 @@
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
 
         // Normally the crop is shrunk from the decor frame
         // to the computed window frame.
@@ -437,8 +439,9 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        final WindowFrames windowFrames = w.getWindowFrames();
+        windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+        w.computeFrameLw();
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -455,7 +458,7 @@
         pf.set(0, 0, logicalWidth, logicalHeight);
         task.mFullscreenForTest = true;
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
         assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
         assertContentFrame(w, cf);
         assertContentInset(w, 0, 0, 0, 0);
@@ -473,9 +476,10 @@
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                 fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
 
-        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+        final WindowFrames windowFrames = w.getWindowFrames();
+        windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
         windowFrames.setDisplayCutout(cutout);
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
 
         assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
         assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
@@ -497,9 +501,10 @@
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                 fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
 
-        final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+        final WindowFrames windowFrames = w.getWindowFrames();
+        windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
         windowFrames.setDisplayCutout(cutout);
-        w.computeFrameLw(windowFrames);
+        w.computeFrameLw();
 
         assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
         assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index b43d9a6..6af3ea7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -391,14 +391,14 @@
     @Test
     public void testDisplayCutoutIsCalculatedRelativeToFrame() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        WindowFrames wf = new WindowFrames();
+        WindowFrames wf = app.getWindowFrames();
         wf.mParentFrame.set(7, 10, 185, 380);
         wf.mDisplayFrame.set(wf.mParentFrame);
         final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
                 Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
         wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
 
-        app.computeFrameLw(wf);
+        app.computeFrameLw();
         assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 95d4a15..659c6e7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -20,6 +20,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.any;
@@ -34,6 +35,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -47,6 +49,9 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.SparseArray;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -68,7 +73,9 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 public class ManagedServicesTest extends UiServiceTestCase {
 
@@ -113,7 +120,12 @@
             when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
         }
         when(mUm.getUsers()).thenReturn(users);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+        IntArray profileIds = new IntArray();
+        profileIds.add(0);
+        profileIds.add(11);
+        profileIds.add(10);
+        profileIds.add(12);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
 
         mExpectedPrimaryPackages = new ArrayMap<>();
         mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
@@ -165,12 +177,12 @@
 
     @Test
     public void testBackupAndRestore_migration_preO() throws Exception {
-        ArrayMap backupPrimaryPackages = new ArrayMap<>();
+        ArrayMap<Integer, String> backupPrimaryPackages = new ArrayMap<>();
         backupPrimaryPackages.put(0, "backup.0:backup:0a");
         backupPrimaryPackages.put(10, "10.backup");
         backupPrimaryPackages.put(11, "eleven");
         backupPrimaryPackages.put(12, "");
-        ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+        ArrayMap<Integer, String> backupPrimaryComponentNames = new ArrayMap<>();
         backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
         backupPrimaryComponentNames.put(10, "again/M1");
         backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
@@ -179,11 +191,11 @@
         backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
         backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
 
-        ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+        ArrayMap<Integer, String> backupSecondaryComponentNames = new ArrayMap<>();
         backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
         backupSecondaryComponentNames.put(10,
                 "this.is.another.package.backup/with.Component:component.backup/2");
-        ArrayMap backupSecondaryPackages = new ArrayMap<>();
+        ArrayMap<Integer, String> backupSecondaryPackages = new ArrayMap<>();
         backupSecondaryPackages.put(0, "");
         backupSecondaryPackages.put(10,
                 "this.is.another.package.backup:package.backup");
@@ -368,7 +380,7 @@
             serializer.endDocument();
             serializer.flush();
 
-            for (int userId : mUserProfiles.getCurrentProfileIds()) {
+            for (int userId : mUserProfiles.getCurrentProfileIds().toArray()) {
                 List<String> expected =
                         stringToList(mExpectedPrimary.get(approvalLevel).get(userId));
                 List<String> actual = stringToList(Settings.Secure.getStringForUser(
@@ -633,7 +645,7 @@
     }
 
     @Test
-    public void testGetAllowedComponents() throws Exception {
+    public void testGetAllowedComponentsByUser() throws Exception {
         ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
                 APPROVAL_BY_COMPONENT);
         loadXml(service);
@@ -708,6 +720,145 @@
         assertTrue(services.isSameUser(service, 10));
     }
 
+    @Test
+    public void testGetAllowedComponents() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+        loadXml(service);
+
+        SparseArray<ArraySet<ComponentName>> expected = new SparseArray<>();
+
+        ArraySet<ComponentName> expected10 = new ArraySet<>();
+        expected10.add(ComponentName.unflattenFromString("this.is.another.package/M1"));
+        expected10.add(ComponentName.unflattenFromString("this.is.another.package/with.Component"));
+        expected10.add(ComponentName.unflattenFromString("component/2"));
+        expected10.add(ComponentName.unflattenFromString("package/component2"));
+        expected.put(10, expected10);
+        ArraySet<ComponentName> expected0 = new ArraySet<>();
+        expected0.add(ComponentName.unflattenFromString("secondary/component.Name"));
+        expected0.add(ComponentName.unflattenFromString("this.is.a.package.name/Ba"));
+        expected0.add(ComponentName.unflattenFromString("another.package/B1"));
+        expected.put(0, expected0);
+        ArraySet<ComponentName> expected12 = new ArraySet<>();
+        expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!"));
+        expected.put(12, expected12);
+        expected.put(11, new ArraySet<>());
+
+        SparseArray<ArraySet<ComponentName>> actual =
+                service.getAllowedComponents(mUserProfiles.getCurrentProfileIds());
+
+        assertContentsInAnyOrder(expected, actual);
+    }
+
+    @Test
+    public void testPopulateComponentsToUnbind_forceRebind() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        IInterface iInterface = mock(IInterface.class);
+        when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+        ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+                mock(ServiceConnection.class), 26);
+        ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+                mock(ServiceConnection.class), 26);
+        Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+        removableBoundServices.add(service0);
+        removableBoundServices.add(service10);
+
+        SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+        Set<ComponentName> allowed0 = new ArraySet<>();
+        allowed0.add(ComponentName.unflattenFromString("a/a"));
+        allowedComponentsToBind.put(0, allowed0);
+        Set<ComponentName> allowed10 = new ArraySet<>();
+        allowed10.add(ComponentName.unflattenFromString("b/b"));
+        allowedComponentsToBind.put(10, allowed10);
+
+        SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        service.populateComponentsToUnbind(true, removableBoundServices, allowedComponentsToBind,
+                componentsToUnbind);
+
+        assertEquals(2, componentsToUnbind.size());
+        assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+        assertTrue(componentsToUnbind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+    }
+
+    @Test
+    public void testPopulateComponentsToUnbind() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        IInterface iInterface = mock(IInterface.class);
+        when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+        ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+                mock(ServiceConnection.class), 26);
+        ManagedServices.ManagedServiceInfo service0a = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("c/c"), 0, false,
+                mock(ServiceConnection.class), 26);
+        ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+                iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+                mock(ServiceConnection.class), 26);
+        Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+        removableBoundServices.add(service0);
+        removableBoundServices.add(service0a);
+        removableBoundServices.add(service10);
+
+        SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+        Set<ComponentName> allowed0 = new ArraySet<>();
+        allowed0.add(ComponentName.unflattenFromString("a/a"));
+        allowedComponentsToBind.put(0, allowed0);
+        Set<ComponentName> allowed10 = new ArraySet<>();
+        allowed10.add(ComponentName.unflattenFromString("b/b"));
+        allowedComponentsToBind.put(10, allowed10);
+
+        SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        service.populateComponentsToUnbind(false, removableBoundServices, allowedComponentsToBind,
+                componentsToUnbind);
+
+        assertEquals(1, componentsToUnbind.size());
+        assertEquals(1, componentsToUnbind.get(0).size());
+        assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
+    }
+
+    @Test
+    public void populateComponentsToBind() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+        ArraySet<ComponentName> allowed0 = new ArraySet<>();
+        allowed0.add(ComponentName.unflattenFromString("a/a"));
+        approvedComponentsByUser.put(0, allowed0);
+        ArraySet<ComponentName> allowed10 = new ArraySet<>();
+        allowed10.add(ComponentName.unflattenFromString("b/b"));
+        allowed10.add(ComponentName.unflattenFromString("c/c"));
+        approvedComponentsByUser.put(10, allowed10);
+        ArraySet<ComponentName> allowed15 = new ArraySet<>();
+        allowed15.add(ComponentName.unflattenFromString("d/d"));
+        approvedComponentsByUser.put(15, allowed15);
+
+        IntArray users = new IntArray();
+        users.add(10);
+        users.add(0);
+
+        SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+        service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+
+        assertEquals(2, componentsToBind.size());
+        assertEquals(1, componentsToBind.get(0).size());
+        assertTrue(componentsToBind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+        assertEquals(2, componentsToBind.get(10).size());
+        assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+        assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
+    }
+
     private void loadXml(ManagedServices service) throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -775,7 +926,8 @@
                 ManagedServices.ENABLED_SERVICES_SEPARATOR)));
     }
 
-    private void assertContentsInAnyOrder(List<?> expected, List<?> actual) {
+    private void assertContentsInAnyOrder(Collection<?> expected, Collection<?> actual) {
+        assertNotNull(actual);
         assertEquals(expected.size(), actual.size());
 
         for (Object o : expected) {
@@ -787,6 +939,21 @@
         }
     }
 
+    private void assertContentsInAnyOrder(SparseArray<ArraySet<ComponentName>> expected,
+            SparseArray<ArraySet<ComponentName>> actual) throws Exception {
+        assertEquals(expected.size(), actual.size());
+
+        for (int i = 0; i < expected.size(); i++) {
+            int key = expected.keyAt(i);
+            assertTrue(actual.indexOfKey(key) >= 0);
+            try {
+                assertContentsInAnyOrder(expected.valueAt(i), actual.get(key));
+            } catch (Throwable t) {
+                throw new Exception("Error validating " + key, t);
+            }
+        }
+    }
+
     private void verifyExpectedBoundEntries(ManagedServices service, boolean primary)
             throws Exception {
         ArrayMap<Integer, String> verifyMap = primary ? mExpectedPrimary.get(service.mApprovalLevel)
@@ -920,7 +1087,7 @@
 
         @Override
         protected boolean checkType(IInterface service) {
-            return false;
+            return true;
         }
 
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f9a4f78..1de1e4e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -33,6 +33,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.os.UserManager;
+import android.util.IntArray;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -103,7 +104,12 @@
         }
         when(mUm.getUsers()).thenReturn(users);
         when(mUm.getUsers(anyBoolean())).thenReturn(users);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+        IntArray profileIds = new IntArray();
+        profileIds.add(0);
+        profileIds.add(11);
+        profileIds.add(10);
+        profileIds.add(12);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 88c6fcf..b955e56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -32,6 +32,7 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.IntArray;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -235,18 +236,22 @@
         mSnoozeHelper.snooze(r2, 1000);
         mSnoozeHelper.snooze(r3, 1000);
         mSnoozeHelper.snooze(r4, 1000);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_SYSTEM});
+        IntArray profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_SYSTEM);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         assertEquals(3, mSnoozeHelper.getSnoozed().size());
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_CURRENT});
+        profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_CURRENT);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         assertEquals(1, mSnoozeHelper.getSnoozed().size());
     }
 
     @Test
     public void testGetSnoozedByUser_managedProfiles() throws Exception {
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_SYSTEM, UserHandle.USER_CURRENT});
+        IntArray profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_CURRENT);
+        profileIds.add(UserHandle.USER_SYSTEM);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
         NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
@@ -273,8 +278,9 @@
 
     @Test
     public void repostGroupSummary_repostsSummary() throws Exception {
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
-                new int[] {UserHandle.USER_SYSTEM});
+        IntArray profileIds = new IntArray();
+        profileIds.add(UserHandle.USER_SYSTEM);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
         NotificationRecord r = getNotificationRecord(
                 "pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
         NotificationRecord r2 = getNotificationRecord(
diff --git a/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
new file mode 100644
index 0000000..03fb123
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/DummyPolicyTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.policy.
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyPolicyTests {
+
+    @Presubmit
+    @Test
+    public void preSubmitTest() {}
+
+    @FlakyTest
+    @Presubmit
+    @Test
+    public void flakyPreSubmitTest() {}
+
+    @Test
+    public void postSubmitTest() {}
+
+    @FlakyTest
+    @Test
+    public void flakyPostSubmitTest() {}
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index c5d6dc7..99ad1f4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,6 +19,8 @@
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.AppGlobals;
 import android.content.ComponentName;
@@ -1128,6 +1130,27 @@
             }
         }
 
+        @Override
+        public void getActiveServiceSupportedActions(List<String> voiceActions,
+                IVoiceActionCheckCallback callback) {
+            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+            synchronized (this) {
+                if (mImpl == null) {
+                    try {
+                        callback.onComplete(null);
+                    } catch (RemoteException e) {
+                    }
+                    return;
+                }
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.getActiveServiceSupportedActions(voiceActions, callback);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+        }
+
         public void onSessionShown() {
             synchronized (this) {
                 final int size = mVoiceInteractionSessionListeners.beginBroadcast();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 57e9f66..61d7d6c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -25,6 +25,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
+
+import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
@@ -57,6 +59,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
     final static String TAG = "VoiceInteractionServiceManager";
@@ -177,6 +180,23 @@
                 activityTokens);
     }
 
+    public void getActiveServiceSupportedActions(List<String> commands,
+            IVoiceActionCheckCallback callback) {
+        if (mService == null) {
+            Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
+            try {
+                callback.onComplete(null);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+        try {
+            mService.getActiveServiceSupportedActions(commands, callback);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "RemoteException while calling getActiveServiceSupportedActions", e);
+        }
+    }
+
     public boolean hideSessionLocked() {
         if (mActiveSession != null) {
             return mActiveSession.hideLocked();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fa24796..b5d1f06 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6217,36 +6217,25 @@
     /**
      * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     public boolean endCall() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.endCall();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#endCall", e);
-        }
         return false;
     }
 
     /**
      * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void answerRingingCall() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.answerRingingCall();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#answerRingingCall", e);
-        }
+
     }
 
     /**
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 6d78990..a20b9b7 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -69,8 +69,8 @@
       return {};
     }
 
-    io::ZeroCopyInputAdaptor adaptor(in.get());
-    if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+    io::ProtoInputStreamReader proto_reader(in.get());
+    if (!proto_reader.ReadMessage(&pb_table)) {
       diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
       return {};
     }
@@ -97,8 +97,8 @@
   }
 
   pb::XmlNode pb_node;
-  io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
-  if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+  io::ProtoInputStreamReader proto_reader(manifest_in.get());
+  if (!proto_reader.ReadMessage(&pb_node)) {
     diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
     return {};
   }
@@ -270,9 +270,9 @@
       return nullptr;
     }
 
-    io::ZeroCopyInputAdaptor adaptor(in.get());
     pb::XmlNode pb_node;
-    if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+    io::ProtoInputStreamReader proto_reader(in.get());
+    if (!proto_reader.ReadMessage(&pb_node)) {
       diag->Error(DiagMessage() << "failed to parse file as proto XML");
       return nullptr;
     }
@@ -317,8 +317,8 @@
     std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
     if (manifest_in != nullptr) {
       pb::XmlNode pb_node;
-      io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
-      if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
+      io::ProtoInputStreamReader proto_reader(manifest_in.get());
+      if (!proto_reader.ReadMessage(&pb_node)) {
         return ApkFormat::kProto;
       }
     }
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 86b1f4c..954f1ed 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -170,8 +170,8 @@
       }
 
       pb::XmlNode pb_node;
-      io::ZeroCopyInputAdaptor adaptor(in.get());
-      if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+      io::ProtoInputStreamReader proto_reader(in.get());
+      if (!proto_reader.ReadMessage(&pb_node)) {
         context_->GetDiagnostics()->Error(DiagMessage(source_)
                                           << "failed to parse proto XML " << *file->path);
         return false;
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index b07fb53..5f978a8 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include "google/protobuf/message_lite.h"
+#include "google/protobuf/io/coded_stream.h"
 
 #include "format/Archive.h"
 #include "io/File.h"
@@ -122,6 +123,23 @@
   io::InputStream* in_;
 };
 
+class ProtoInputStreamReader {
+ public:
+  explicit ProtoInputStreamReader(io::InputStream* in) : in_(in) { }
+
+  /** Deserializes a MessageLite proto from the current position in the input stream.*/
+  template <typename T> bool ReadMessage(T *message_lite) {
+    ZeroCopyInputAdaptor adapter(in_);
+    google::protobuf::io::CodedInputStream coded_stream(&adapter);
+    coded_stream.SetTotalBytesLimit(std::numeric_limits<int32_t>::max(),
+                                    coded_stream.BytesUntilTotalBytesLimit());
+    return message_lite->ParseFromCodedStream(&coded_stream);
+  }
+
+ private:
+  io::InputStream* in_;
+};
+
 }  // namespace io
 }  // namespace aapt