Merge "PaintMeasureTextTest: only pause/resume not_cached benchmarks"
diff --git a/Android.mk b/Android.mk
index 7a3c46d..a69170f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -219,6 +219,8 @@
 	core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
 	core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
 	core/java/android/hardware/location/IContextHubCallback.aidl \
+	core/java/android/hardware/location/IContextHubClient.aidl \
+	core/java/android/hardware/location/IContextHubClientCallback.aidl \
 	core/java/android/hardware/location/IContextHubService.aidl \
 	core/java/android/hardware/location/IContextHubTransactionCallback.aidl \
 	core/java/android/hardware/radio/IRadioService.aidl \
@@ -527,11 +529,13 @@
 	telephony/java/com/android/ims/internal/IImsService.aidl \
 	telephony/java/com/android/ims/internal/IImsServiceController.aidl \
 	telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl \
+        telephony/java/com/android/ims/internal/IImsSmsFeature.aidl \
 	telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \
 	telephony/java/com/android/ims/internal/IImsUt.aidl \
 	telephony/java/com/android/ims/internal/IImsUtListener.aidl \
 	telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl \
 	telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl \
+        telephony/java/com/android/ims/internal/ISmsListener.aidl \
         telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl \
         telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl \
         telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl \
@@ -1114,7 +1118,7 @@
 		-federate SupportLib https://developer.android.com \
 		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
 
-# ====  the api diff ===========================
+# ====  Public API diff ===========================
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
@@ -1139,6 +1143,31 @@
 
 include $(BUILD_APIDIFF)
 
+# ====  System API diff ===========================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
+LOCAL_INTERMEDIATE_SOURCES := $(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+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
+
+last_released_sdk_version := $(lastword $(call numerically_sort, \
+			$(filter-out current, \
+				$(patsubst $(SRC_SYSTEM_API_DIR)/%.txt,%, $(wildcard $(SRC_SYSTEM_API_DIR)/*.txt)) \
+			 )\
+		))
+
+LOCAL_APIDIFF_OLDAPI := $(LOCAL_PATH)/../../$(SRC_SYSTEM_API_DIR)/$(last_released_sdk_version)
+LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE))
+
+include $(BUILD_APIDIFF)
+
 # ====  the api stubs and current.xml ===========================
 include $(CLEAR_VARS)
 
@@ -1315,7 +1344,7 @@
 $(full_target): $(framework_built)
 
 
-# ====  static html in the sdk ==================================
+# ==== Public API static reference docs ==================================
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
@@ -1357,6 +1386,50 @@
 $(full_target): $(framework_built)
 
 
+# ==== System API static reference docs ==================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
+LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
+LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
+LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
+
+LOCAL_MODULE := offline-system-sdk-referenceonly
+
+LOCAL_DROIDDOC_OPTIONS:=\
+		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+		-hide 101 -hide 104 -hide 108 \
+		-showAnnotation android.annotation.SystemApi \
+		-offlinemode \
+		-title "Android System SDK" \
+		-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
+		-sdkvalues $(OUT_DOCS) \
+		-hdf android.whichdoc offline \
+		-referenceonly
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
+
+include $(BUILD_DROIDDOC)
+
+static_doc_index_redirect := $(out_dir)/index.html
+$(static_doc_index_redirect): $(LOCAL_PATH)/docs/docs-documentation-redirect.html
+	$(copy-file-to-target)
+
+static_doc_properties := $(out_dir)/source.properties
+$(static_doc_properties): \
+	$(LOCAL_PATH)/docs/source.properties | $(ACP)
+	$(hide) mkdir -p $(dir $@)
+	$(hide) $(ACP) $< $@
+
+$(full_target): $(static_doc_index_redirect)
+$(full_target): $(static_doc_properties)
+$(full_target): $(framework_built)
+
+
 # ==== docs for the web (on the androiddevdocs app engine server) =======================
 include $(CLEAR_VARS)
 
diff --git a/api/current.txt b/api/current.txt
index 7c22491..182dd3a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22033,6 +22033,7 @@
     method public android.media.Image acquireLatestImage();
     method public android.media.Image acquireNextImage();
     method public void close();
+    method public void discardFreeBuffers();
     method public int getHeight();
     method public int getImageFormat();
     method public int getMaxImages();
@@ -33139,6 +33140,7 @@
   public final class AlarmClock {
     ctor public AlarmClock();
     field public static final java.lang.String ACTION_DISMISS_ALARM = "android.intent.action.DISMISS_ALARM";
+    field public static final java.lang.String ACTION_DISMISS_TIMER = "android.intent.action.DISMISS_TIMER";
     field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
     field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
     field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
@@ -47906,6 +47908,7 @@
     method public void interrupt();
     method public static boolean isAccessibilityButtonSupported();
     method public boolean isEnabled();
+    method public boolean isObservedEventType(int);
     method public boolean isTouchExplorationEnabled();
     method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
     method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
@@ -49091,15 +49094,19 @@
 
   public abstract interface TextClassifier {
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options);
+    method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int);
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
     method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options);
+    method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
+    method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
     field public static final android.view.textclassifier.TextClassifier NO_OP;
     field public static final java.lang.String TYPE_ADDRESS = "address";
     field public static final java.lang.String TYPE_EMAIL = "email";
     field public static final java.lang.String TYPE_OTHER = "other";
     field public static final java.lang.String TYPE_PHONE = "phone";
+    field public static final java.lang.String TYPE_UNKNOWN = "";
     field public static final java.lang.String TYPE_URL = "url";
   }
 
@@ -49115,13 +49122,9 @@
   }
 
   public static final class TextLinks.Options {
+    ctor public TextLinks.Options();
     method public android.os.LocaleList getDefaultLocales();
-  }
-
-  public static final class TextLinks.Options.Builder {
-    ctor public TextLinks.Options.Builder();
-    method public android.view.textclassifier.TextLinks.Options build();
-    method public android.view.textclassifier.TextLinks.Options.Builder setLocaleList(android.os.LocaleList);
+    method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList);
   }
 
   public static final class TextLinks.TextLink {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8647ed3..b181538 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -958,6 +958,15 @@
 
 package android.view.accessibility {
 
+  public final class AccessibilityManager {
+    method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
+    method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
+  }
+
+  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
+    method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
+  }
+
   public class AccessibilityNodeInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
   }
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 3172281..6ded246 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -159,7 +159,7 @@
     void const* mapbase = MAP_FAILED;
     ssize_t mapsize = -1;
 
-    void* base = NULL;
+    void const* base = NULL;
     uint32_t w, s, h, f;
     android_dataspace d;
     size_t size = 0;
@@ -179,6 +179,7 @@
     ProcessState::self()->setThreadPoolMaxThreadCount(0);
     ProcessState::self()->startThreadPool();
 
+    ScreenshotClient screenshot;
     sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
     if (display == NULL) {
         fprintf(stderr, "Unable to get handle for display %d\n", displayId);
@@ -198,57 +199,51 @@
     uint8_t displayOrientation = configs[activeConfig].orientation;
     uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
 
-    sp<GraphicBuffer> outBuffer;
-    status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
-            0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
-            &outBuffer);
-    if (result != NO_ERROR) {
-        close(fd);
-        _exit(1);
+    status_t result = screenshot.update(display, Rect(),
+            0 /* reqWidth */, 0 /* reqHeight */,
+            INT32_MIN, INT32_MAX, /* all layers */
+            false, captureOrientation);
+    if (result == NO_ERROR) {
+        base = screenshot.getPixels();
+        w = screenshot.getWidth();
+        h = screenshot.getHeight();
+        s = screenshot.getStride();
+        f = screenshot.getFormat();
+        d = screenshot.getDataSpace();
+        size = screenshot.getSize();
     }
 
-    result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
-
-    if (base == NULL) {
-        close(fd);
-        _exit(1);
-    }
-
-    w = outBuffer->getWidth();
-    h = outBuffer->getHeight();
-    s = outBuffer->getStride();
-    f = outBuffer->getPixelFormat();
-    d = HAL_DATASPACE_UNKNOWN;
-    size = s * h * bytesPerPixel(f);
-
-    if (png) {
-        const SkImageInfo info =
-            SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, dataSpaceToColorSpace(d));
-        SkPixmap pixmap(info, base, s * bytesPerPixel(f));
-        struct FDWStream final : public SkWStream {
-          size_t fBytesWritten = 0;
-          int fFd;
-          FDWStream(int f) : fFd(f) {}
-          size_t bytesWritten() const override { return fBytesWritten; }
-          bool write(const void* buffer, size_t size) override {
-            fBytesWritten += size;
-            return size == 0 || ::write(fFd, buffer, size) > 0;
-          }
-        } fdStream(fd);
-        (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
-        if (fn != NULL) {
-            notifyMediaScanner(fn);
-        }
-    } else {
-        uint32_t c = dataSpaceToInt(d);
-        write(fd, &w, 4);
-        write(fd, &h, 4);
-        write(fd, &f, 4);
-        write(fd, &c, 4);
-        size_t Bpp = bytesPerPixel(f);
-        for (size_t y=0 ; y<h ; y++) {
-            write(fd, base, w*Bpp);
-            base = (void *)((char *)base + s*Bpp);
+    if (base != NULL) {
+        if (png) {
+            const SkImageInfo info =
+                SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType,
+                    dataSpaceToColorSpace(d));
+            SkPixmap pixmap(info, base, s * bytesPerPixel(f));
+            struct FDWStream final : public SkWStream {
+              size_t fBytesWritten = 0;
+              int fFd;
+              FDWStream(int f) : fFd(f) {}
+              size_t bytesWritten() const override { return fBytesWritten; }
+              bool write(const void* buffer, size_t size) override {
+                fBytesWritten += size;
+                return size == 0 || ::write(fFd, buffer, size) > 0;
+              }
+            } fdStream(fd);
+            (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
+            if (fn != NULL) {
+                notifyMediaScanner(fn);
+            }
+        } else {
+            uint32_t c = dataSpaceToInt(d);
+            write(fd, &w, 4);
+            write(fd, &h, 4);
+            write(fd, &f, 4);
+            write(fd, &c, 4);
+            size_t Bpp = bytesPerPixel(f);
+            for (size_t y=0 ; y<h ; y++) {
+                write(fd, base, w*Bpp);
+                base = (void *)((char *)base + s*Bpp);
+            }
         }
     }
     close(fd);
@@ -258,4 +253,4 @@
 
     // b/36066697: Avoid running static destructors.
     _exit(0);
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 57b4fc1..2fd7947 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -220,7 +220,7 @@
                                          const unique_ptr<MetricsManager>& metricsManager) {
     std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
 
-    size_t totalBytes = metricsManager->byteSize();
+    size_t totalBytes = metricsManager->byteSize() + mUidMap->getBytesUsed();
     if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data.
         auto lastFlushNs = mLastBroadcastTimes.find(key);
         if (lastFlushNs != mLastBroadcastTimes.end()) {
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c019d5d..7eca5aa 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -480,7 +480,7 @@
         fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
                 mProcessor->GetMetricsSize(key));
     }
-    fprintf(out, "Detailed statsd stats in logcat...");
+    fprintf(out, "Detailed statsd stats in logcat...\n");
     StatsdStats& statsdStats = StatsdStats::getInstance();
     bool reset = false;
     if (args.size() > 1) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 20e7c60..e0d9ce7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -91,6 +91,8 @@
         CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
         CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
         CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
+        WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011;
+        ModemActivityInfoPulled modem_activity_info_pulled = 1012;
     }
 }
 
@@ -699,7 +701,7 @@
     optional int32 user = 7;
 }
 
-/*
+/**
  * Logs activity going to foreground or background
  *
  * Logged from:
@@ -839,7 +841,7 @@
     optional int64 time = 4;
 }
 
-/*
+/**
  * Pulls PowerStatePlatformSleepState.
  *
  * Definition here:
@@ -899,7 +901,7 @@
     optional int32 is_create = 3;
 }
 
-/*
+/**
  * Pulls Cpu time per frequency.
  * Note: this should be pulled for gauge metric only, without condition.
  * The puller keeps internal state of last values. It should not be pulled by
@@ -916,7 +918,7 @@
     optional uint64 time = 3;
 }
 
-/*
+/**
  * Pulls Cpu Time Per Uid.
  * Note that isolated process uid time should be attributed to host uids.
  */
@@ -965,3 +967,56 @@
     // The destination port if this was a TCP or UDP packet.
     optional int32 destination_port = 9;
 }
+
+/**
+ * Pulls Wifi Controller Activity Energy Info
+ */
+message WifiActivityEnergyInfoPulled {
+    // timestamp(wall clock) of record creation
+    optional uint64 timestamp_ms = 1;
+    // stack reported state
+    // TODO: replace this with proto enum
+    optional int32 stack_state = 2;
+    // tx time in ms
+    optional uint64 controller_tx_time_ms = 3;
+    // rx time in ms
+    optional uint64 controller_rx_time_ms = 4;
+    // idle time in ms
+    optional uint64 controller_idle_time_ms = 5;
+    // product of current(mA), voltage(V) and time(ms)
+    optional uint64 controller_energy_used = 6;
+}
+
+/**
+ * Pulls Modem Activity Energy Info
+ */
+message ModemActivityInfoPulled {
+    // timestamp(wall clock) of record creation
+    optional uint64 timestamp_ms = 1;
+    // sleep time in ms.
+    optional uint64 sleep_time_ms = 2;
+    // idle time in ms
+    optional uint64 controller_idle_time_ms = 3;
+    /**
+     * Tx power index
+     * index 0 = tx_power < 0dBm
+     * index 1 = 0dBm < tx_power < 5dBm
+     * index 2 = 5dBm < tx_power < 15dBm
+     * index 3 = 15dBm < tx_power < 20dBm
+     * index 4 = tx_power > 20dBm
+     */
+    // tx time in ms at power level 0
+    optional uint64 controller_tx_time_pl0_ms = 4;
+    // tx time in ms at power level 1
+    optional uint64 controller_tx_time_pl1_ms = 5;
+    // tx time in ms at power level 2
+    optional uint64 controller_tx_time_pl2_ms = 6;
+    // tx time in ms at power level 3
+    optional uint64 controller_tx_time_pl3_ms = 7;
+    // tx time in ms at power level 4
+    optional uint64 controller_tx_time_pl4_ms = 8;
+    // rx time in ms at power level 5
+    optional uint64 controller_rx_time_ms = 9;
+    // product of current(mA), voltage(V) and time(ms)
+    optional uint64 energy_used = 10;
+}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 2bd3612..2957457 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -45,6 +45,7 @@
 const int FIELD_ID_CONDITION_STATS = 5;
 const int FIELD_ID_METRIC_STATS = 6;
 const int FIELD_ID_ATOM_STATS = 7;
+const int FIELD_ID_UIDMAP_STATS = 8;
 
 const int FIELD_ID_MATCHER_STATS_NAME = 1;
 const int FIELD_ID_MATCHER_STATS_COUNT = 2;
@@ -173,6 +174,27 @@
     it->second.add_dump_report_time_sec(timeSec);
 }
 
+void StatsdStats::noteUidMapDropped(int snapshots, int deltas) {
+    lock_guard<std::mutex> lock(mLock);
+    mUidMapStats.set_dropped_snapshots(mUidMapStats.dropped_snapshots() + snapshots);
+    mUidMapStats.set_dropped_changes(mUidMapStats.dropped_changes() + deltas);
+}
+
+void StatsdStats::setUidMapSnapshots(int snapshots) {
+    lock_guard<std::mutex> lock(mLock);
+    mUidMapStats.set_snapshots(snapshots);
+}
+
+void StatsdStats::setUidMapChanges(int changes) {
+    lock_guard<std::mutex> lock(mLock);
+    mUidMapStats.set_changes(changes);
+}
+
+void StatsdStats::setCurrentUidMapMemory(int bytes) {
+    lock_guard<std::mutex> lock(mLock);
+    mUidMapStats.set_bytes_used(bytes);
+}
+
 void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) {
     lock_guard<std::mutex> lock(mLock);
     // if name doesn't exist before, it will create the key with count 0.
@@ -364,6 +386,15 @@
         }
     }
 
+    const int numBytes = mUidMapStats.ByteSize();
+    vector<char> buffer(numBytes);
+    mUidMapStats.SerializeToArray(&buffer[0], numBytes);
+    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size());
+    VLOG("UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
+         "lost=%d",
+         mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(),
+         mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes());
+
     output->clear();
     size_t bufferSize = proto.size();
     output->resize(bufferSize);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 451144f..d6f6566 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -45,6 +45,10 @@
 
     const static int kMaxTimestampCount = 20;
 
+    // Cap the UID map's memory usage to this. This should be fairly high since the UID information
+    // is critical for understanding the metrics.
+    const static size_t kMaxBytesUsedUidMap = 50 * 1024;
+
     /**
      * Report a new config has been received and report the static stats about the config.
      *
@@ -113,6 +117,18 @@
     void noteAtomLogged(int atomId, int32_t timeSec);
 
     /**
+     * Records the number of snapshot and delta entries that are being dropped from the uid map.
+     */
+    void noteUidMapDropped(int snapshots, int deltas);
+
+    /**
+     * Updates the number of snapshots currently stored in the uid map.
+     */
+    void setUidMapSnapshots(int snapshots);
+    void setUidMapChanges(int changes);
+    void setCurrentUidMapMemory(int bytes);
+
+    /**
      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
      * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
      * to collect stats after reset() has been called.
@@ -133,6 +149,9 @@
 
     int32_t mStartTimeSec;
 
+    // Track the number of dropped entries used by the uid map.
+    StatsdStatsReport_UidMapStats mUidMapStats;
+
     // The stats about the configs that are still in use.
     std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 4338399..0064240 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -94,8 +94,6 @@
 }
 
 void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-
     mProto = std::make_unique<ProtoOutputStream>();
     mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -109,8 +107,13 @@
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
-void CountMetricProducer::serializeBuckets() {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
+    long long endTime = time(nullptr) * NS_PER_SEC;
+
+    // Dump current bucket if it's stale.
+    // If current bucket is still on-going, don't force dump current bucket.
+    // In finish(), We can force dump current bucket.
+    flushIfNeeded(endTime);
     VLOG("metric %s dump report now...", mMetric.name().c_str());
 
     for (const auto& counter : mPastBuckets) {
@@ -156,40 +159,28 @@
         }
         mProto->end(wrapperToken);
     }
+
     mProto->end(mProtoToken);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
 
-    mPastBuckets.clear();
-    // TODO: Clear mDimensionKeyMap once the report is dumped.
-}
-
-std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
-    long long endTime = time(nullptr) * NS_PER_SEC;
     VLOG("metric %s dump report now...", mMetric.name().c_str());
-    // Dump current bucket if it's stale.
-    // If current bucket is still on-going, don't force dump current bucket.
-    // In finish(), We can force dump current bucket.
-    flushIfNeeded(endTime);
-
-    // TODO(yanglu): merge these three functions to one to avoid three locks.
-    serializeBuckets();
-
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
+    mPastBuckets.clear();
 
     return buffer;
+
+    // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
     VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     mCondition = conditionMet;
 }
 
 bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
         return false;
     }
@@ -217,40 +208,38 @@
 
     flushIfNeeded(eventTimeNs);
 
-    // ===========GuardRail==============
-    if (hitGuardRail(eventKey)) {
+    if (condition == false) {
         return;
     }
 
-    // TODO(yanglu): move the following logic to a seperate function to make it lockable.
-    {
-        std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-        if (condition == false) {
+    auto it = mCurrentSlicedCounter->find(eventKey);
+
+    if (it == mCurrentSlicedCounter->end()) {
+        // ===========GuardRail==============
+        if (hitGuardRail(eventKey)) {
             return;
         }
 
-        auto it = mCurrentSlicedCounter->find(eventKey);
-        if (it == mCurrentSlicedCounter->end()) {
-            // create a counter for the new key
-            mCurrentSlicedCounter->insert({eventKey, 1});
-        } else {
-            // increment the existing value
-            auto& count = it->second;
-            count++;
-        }
-        const int64_t& count = mCurrentSlicedCounter->find(eventKey)->second;
-        for (auto& tracker : mAnomalyTrackers) {
-            tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, count);
-        }
-        VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), (long long)(count));
+        // create a counter for the new key
+        (*mCurrentSlicedCounter)[eventKey] = 1;
+    } else {
+        // increment the existing value
+        auto& count = it->second;
+        count++;
     }
+
+    for (auto& tracker : mAnomalyTrackers) {
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+                                         mCurrentSlicedCounter->find(eventKey)->second);
+    }
+
+    VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
+         (long long)(*mCurrentSlicedCounter)[eventKey]);
 }
 
 // When a new matched event comes in, we check if event falls into the current
 // bucket. If not, flush the old counter to past buckets and initialize the new bucket.
 void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-
     if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return;
     }
@@ -284,7 +273,6 @@
 // greater than actual data size as it contains each dimension of
 // CountMetricData is  duplicated.
 size_t CountMetricProducer::byteSize() const {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     size_t totalSize = 0;
     for (const auto& pair : mPastBuckets) {
         totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 164dfb2..f78a199 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -75,8 +75,6 @@
     void startNewProtoOutputStream(long long timestamp) override;
 
 private:
-    void serializeBuckets();
-
     const CountMetric mMetric;
 
     // TODO: Add a lock to mPastBuckets.
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3b49b9a..a0374c0 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -104,7 +104,6 @@
 }
 
 void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     mProto = std::make_unique<ProtoOutputStream>();
     mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -112,7 +111,7 @@
 }
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
-        const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const {
+        const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
@@ -131,7 +130,6 @@
 }
 
 void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
     flushIfNeeded(eventTime);
     // Now for each of the on-going event, check if the condition has changed for them.
@@ -141,7 +139,6 @@
 }
 
 void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
     mCondition = conditionMet;
     flushIfNeeded(eventTime);
@@ -152,8 +149,15 @@
     }
 }
 
-void DurationMetricProducer::SerializeBuckets() {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
+    long long endTime = time(nullptr) * NS_PER_SEC;
+
+    // Dump current bucket if it's stale.
+    // If current bucket is still on-going, don't force dump current bucket.
+    // In finish(), We can force dump current bucket.
+    flushIfNeeded(endTime);
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
+
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
         VLOG("  dimension key %s", hashableKey.c_str());
@@ -210,29 +214,13 @@
     mProto->end(mProtoToken);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
-}
-
-std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
-    VLOG("metric %s dump report now...", mMetric.name().c_str());
-
-    long long endTime = time(nullptr) * NS_PER_SEC;
-
-    // Dump current bucket if it's stale.
-    // If current bucket is still on-going, don't force dump current bucket.
-    // In finish(), We can force dump current bucket.
-    flushIfNeeded(endTime);
-
-    SerializeBuckets();
-
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
-
     startNewProtoOutputStream(endTime);
     // TODO: Properly clear the old buckets.
     return buffer;
 }
 
 void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
         return;
     }
@@ -252,7 +240,6 @@
 }
 
 bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     // the key is not new, we are good.
     if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
         return false;
@@ -278,37 +265,32 @@
         const LogEvent& event, bool scheduledPull) {
     flushIfNeeded(event.GetTimestampNs());
 
-    // TODO(yanglu): move the following logic to a seperate function to make it lockable.
-    {
-        std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-        if (matcherIndex == mStopAllIndex) {
-            for (auto& pair : mCurrentSlicedDuration) {
-                pair.second->noteStopAll(event.GetTimestampNs());
-            }
+    if (matcherIndex == mStopAllIndex) {
+        for (auto& pair : mCurrentSlicedDuration) {
+            pair.second->noteStopAll(event.GetTimestampNs());
+        }
+        return;
+    }
+
+    HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
+
+    if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+        if (hitGuardRail(eventKey)) {
             return;
         }
+        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
+    }
 
-        HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
+    auto it = mCurrentSlicedDuration.find(eventKey);
 
-        if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
-            if (hitGuardRail(eventKey)) {
-                return;
-            }
-            mCurrentSlicedDuration[eventKey] =
-                    createDurationTracker(eventKey, mPastBuckets[eventKey]);
-        }
-        auto it = mCurrentSlicedDuration.find(eventKey);
-
-        if (matcherIndex == mStartIndex) {
-            it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
-        } else if (matcherIndex == mStopIndex) {
-            it->second->noteStop(atomKey, event.GetTimestampNs(), false);
-        }
+    if (matcherIndex == mStartIndex) {
+        it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
+    } else if (matcherIndex == mStopIndex) {
+        it->second->noteStop(atomKey, event.GetTimestampNs(), false);
     }
 }
 
 size_t DurationMetricProducer::byteSize() const {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     size_t totalSize = 0;
     for (const auto& pair : mPastBuckets) {
         totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 68fff48..5b5373e 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -71,8 +71,6 @@
     void startNewProtoOutputStream(long long timestamp) override;
 
 private:
-    void SerializeBuckets();
-
     const DurationMetric mMetric;
 
     // Index of the SimpleLogEntryMatcher which defines the start.
@@ -98,8 +96,8 @@
     std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
             mCurrentSlicedDuration;
 
-    std::unique_ptr<DurationTracker> createDurationTracker(
-            const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const;
+    std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
+                                                           std::vector<DurationBucket>& bucket);
     bool hitGuardRail(const HashableDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(DurationBucket{});
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 53f112a..95a18f7 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -73,7 +73,6 @@
 }
 
 void EventMetricProducer::startNewProtoOutputStream(long long startTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     mProto = std::make_unique<ProtoOutputStream>();
     // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
     // and StatsEvent.
@@ -90,16 +89,11 @@
 
 std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
     long long endTime = time(nullptr) * NS_PER_SEC;
-    // TODO(yanglu): make this section to an util function.
-    {
-        std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-        mProto->end(mProtoToken);
-        mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
+    mProto->end(mProtoToken);
+    mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
 
-        size_t bufferSize = mProto->size();
-        VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
-    }
-
+    size_t bufferSize = mProto->size();
+    VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
@@ -109,7 +103,6 @@
 
 void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
     VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     mCondition = conditionMet;
 }
 
@@ -117,7 +110,6 @@
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
         const LogEvent& event, bool scheduledPull) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     if (!condition) {
         return;
     }
@@ -132,7 +124,6 @@
 }
 
 size_t EventMetricProducer::byteSize() const {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     return mProto->bytesWritten();
 }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index ed4c760..1791654 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -102,7 +102,6 @@
 }
 
 void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     mProto = std::make_unique<ProtoOutputStream>();
     mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -112,8 +111,14 @@
 void GaugeMetricProducer::finish() {
 }
 
-void GaugeMetricProducer::SerializeBuckets() {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
+    VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
+
+    // Dump current bucket if it's stale.
+    // If current bucket is still on-going, don't force dump current bucket.
+    // In finish(), We can force dump current bucket.
+    flushIfNeeded(time(nullptr) * NS_PER_SEC);
+
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
         auto it = mDimensionKeyMap.find(hashableKey);
@@ -161,69 +166,50 @@
     mProto->end(mProtoToken);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
-    mPastBuckets.clear();
-}
-
-std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
-    VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
-
-    // Dump current bucket if it's stale.
-    // If current bucket is still on-going, don't force dump current bucket.
-    // In finish(), We can force dump current bucket.
-    flushIfNeeded(time(nullptr) * NS_PER_SEC);
-
-    SerializeBuckets();
 
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
+    mPastBuckets.clear();
+
     return buffer;
 
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
+    AutoMutex _l(mLock);
     VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
-
-    // flushIfNeeded holds the write lock and is thread-safe.
     flushIfNeeded(eventTime);
+    mCondition = conditionMet;
 
-    vector<std::shared_ptr<LogEvent>> allData;
-    // The following section is to update the condition and re-pull the gauge.
-    // TODO(yanglu): make it a seperate lockable function.
-    {
-        std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-
-        mCondition = conditionMet;
-
-        // Push mode. No need to proactively pull the gauge data.
-        if (mPullTagId == -1) {
-            return;
-        }
-        if (!mCondition) {
-            return;
-        }
-        // Already have gauge metric for the current bucket, do not do it again.
-        if (mCurrentSlicedBucket->size() > 0) {
-            return;
-        }
-        if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
-            ALOGE("Stats puller failed for tag: %d", mPullTagId);
-            return;
-        }
+    // Push mode. No need to proactively pull the gauge data.
+    if (mPullTagId == -1) {
+        return;
     }
-
-    // onMatchedLogEventInternal holds the write lock and is thread-safe.
+    if (!mCondition) {
+        return;
+    }
+    // Already have gauge metric for the current bucket, do not do it again.
+    if (mCurrentSlicedBucket->size() > 0) {
+        return;
+    }
+    vector<std::shared_ptr<LogEvent>> allData;
+    if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
+        ALOGE("Stats puller failed for tag: %d", mPullTagId);
+        return;
+    }
     for (const auto& data : allData) {
         onMatchedLogEvent(0, *data, false /*scheduledPull*/);
     }
+    flushIfNeeded(eventTime);
 }
 
 void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
-int64_t GaugeMetricProducer::getGauge(const LogEvent& event) const {
+int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
     status_t err = NO_ERROR;
     int64_t val = event.GetLong(mMetric.gauge_field(), &err);
     if (err == NO_ERROR) {
@@ -235,14 +221,13 @@
 }
 
 void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
-    // onMatchedLogEventInternal holds the write lock and is thread-safe.
+    AutoMutex mutex(mLock);
     for (const auto& data : allData) {
         onMatchedLogEvent(0, *data, true /*scheduledPull*/);
     }
 }
 
 bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
         return false;
     }
@@ -266,32 +251,32 @@
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
         const LogEvent& event, bool scheduledPull) {
-    uint64_t eventTimeNs = event.GetTimestampNs();
-    flushIfNeeded(eventTimeNs);
-
     if (condition == false) {
         return;
     }
-    const long gauge = getGauge(event);
-    if (gauge < 0) {
-        return;
-    }
-    if (hitGuardRail(eventKey)) {
-        return;
-    }
-
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+    uint64_t eventTimeNs = event.GetTimestampNs();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
         return;
     }
 
+    // When the event happens in a new bucket, flush the old buckets.
+    if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
+        flushIfNeeded(eventTimeNs);
+    }
+
     // For gauge metric, we just simply use the first gauge in the given bucket.
     if (!mCurrentSlicedBucket->empty()) {
         return;
     }
+    const long gauge = getGauge(event);
+    if (gauge >= 0) {
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
         (*mCurrentSlicedBucket)[eventKey] = gauge;
+    }
     for (auto& tracker : mAnomalyTrackers) {
         tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
     }
@@ -303,7 +288,6 @@
 // if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
 // the GaugeMetricProducer while holding the lock.
 void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return;
     }
@@ -337,7 +321,6 @@
 }
 
 size_t GaugeMetricProducer::byteSize() const {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     size_t totalSize = 0;
     for (const auto& pair : mPastBuckets) {
         totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 8df6111..f344303 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -81,8 +81,6 @@
     void startNewProtoOutputStream(long long timestamp) override;
 
 private:
-    void SerializeBuckets();
-
     // The default bucket size for gauge metric is 1 second.
     static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
     const GaugeMetric mMetric;
@@ -91,6 +89,8 @@
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
 
+    Mutex mLock;
+
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
@@ -98,7 +98,7 @@
     // The current bucket.
     std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>();
 
-    int64_t getGauge(const LogEvent& event) const;
+    int64_t getGauge(const LogEvent& event);
 
     bool hitGuardRail(const HashableDimensionKey& newKey);
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 7542a94..62fb632 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -23,7 +23,6 @@
 
 void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event,
                                        bool scheduledPull) {
-    std::unique_lock<std::shared_timed_mutex> writeLock(mRWMutex);
     uint64_t eventTimeNs = event.GetTimestampNs();
     // this is old event, maybe statsd restarted?
     if (eventTimeNs < mStartTimeNs) {
@@ -60,16 +59,12 @@
     } else {
         condition = mCondition;
     }
-    // Unlock as onMatchedLogEventInternal is threadsafe.
-    writeLock.unlock();
 
     onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event,
                               scheduledPull);
 }
 
 std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-
     size_t bufferSize = mProto->size();
 
     std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 27343ad..b22ff6f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,8 +17,6 @@
 #ifndef METRIC_PRODUCER_H
 #define METRIC_PRODUCER_H
 
-#include <shared_mutex>
-
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "config/ConfigKey.h"
@@ -139,10 +137,6 @@
 
     long long mProtoToken;
 
-    // Read/Write mutex to make the producer thread-safe.
-    // TODO(yanglu): replace with std::shared_mutex when available in libc++.
-    mutable std::shared_timed_mutex mRWMutex;
-
     virtual void startNewProtoOutputStream(long long timestamp) = 0;
 
     std::unique_ptr<std::vector<uint8_t>> serializeProto();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index eed7841..66c8419 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -121,7 +121,6 @@
 }
 
 void ValueMetricProducer::startNewProtoOutputStream(long long startTime) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     mProto = std::make_unique<ProtoOutputStream>();
     mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -137,8 +136,9 @@
     VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
-void ValueMetricProducer::SerializeBuckets() {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
+
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
         VLOG("  dimension key %s", hashableKey.c_str());
@@ -185,63 +185,47 @@
     mProto->end(mProtoToken);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
-    mPastBuckets.clear();
-}
 
-std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
     VLOG("metric %s dump report now...", mMetric.name().c_str());
-
-    SerializeBuckets();
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
+    mPastBuckets.clear();
 
     return buffer;
+
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
-    vector<shared_ptr<LogEvent>> allData;
+    AutoMutex _l(mLock);
+    mCondition = condition;
 
-    // TODO(yanglu): move the following logic to a seperate function to make it lockable.
-    {
-        std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-        mCondition = condition;
-
-        if (mPullTagId == -1) {
-            return;
-        }
-
+    if (mPullTagId != -1) {
         if (mCondition == true) {
             mStatsPullerManager->RegisterReceiver(mPullTagId, this,
                                                   mMetric.bucket().bucket_size_millis());
         } else if (mCondition == false) {
             mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
         }
-        if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
-            return;
-        }
-    }
 
-    if (allData.size() == 0) {
+        vector<shared_ptr<LogEvent>> allData;
+        if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
+            if (allData.size() == 0) {
+                return;
+            }
+            for (const auto& data : allData) {
+                onMatchedLogEvent(0, *data, false);
+            }
+            flushIfNeeded(eventTime);
+        }
         return;
     }
-
-    // onMatchedLogEventInternal holds the write lock and is thread-safe.
-    for (const auto& data : allData) {
-        onMatchedLogEvent(0, *data, false);
-    }
-    // flushIfNeeded holds the write lock and is thread-safe.
-    flushIfNeeded(eventTime);
-}
-
-bool ValueMetricProducer::IsConditionMet() const {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
-    return mCondition == true || !mMetric.has_condition();
 }
 
 void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
-    if (IsConditionMet()) {
+    AutoMutex _l(mLock);
+    if (mCondition == true || !mMetric.has_condition()) {
         if (allData.size() == 0) {
             return;
         }
@@ -258,7 +242,6 @@
 }
 
 bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     // ===========GuardRail==============
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -279,75 +262,58 @@
     return false;
 }
 
-void ValueMetricProducer::onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs,
-                                                         const HashableDimensionKey& eventKey,
-                                                         const long& value, bool scheduledPull) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-
-    if (eventTimeNs < mCurrentBucketStartTimeNs) {
-        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
-             (long long)mCurrentBucketStartTimeNs);
-        return;
-    }
-    Interval& interval = mCurrentSlicedBucket[eventKey];
-    if (scheduledPull) {
-        // scheduled pull always sets beginning of current bucket and end
-        // of next bucket
-        if (interval.raw.size() > 0) {
-            interval.raw.back().second = value;
-        } else {
-            interval.raw.push_back(make_pair(value, value));
-        }
-        Interval& nextInterval = mNextSlicedBucket[eventKey];
-        if (nextInterval.raw.size() == 0) {
-            nextInterval.raw.push_back(make_pair(value, 0));
-        } else {
-            nextInterval.raw.front().first = value;
-        }
-    } else {
-        if (mCondition == true) {
-            interval.raw.push_back(make_pair(value, 0));
-        } else {
-            if (interval.raw.size() != 0) {
-                interval.raw.back().second = value;
-            } else {
-                interval.tainted = true;
-                VLOG("Data on condition true missing!");
-            }
-        }
-    }
-}
-
-void ValueMetricProducer::onMatchedLogEventInternal_push(const uint64_t& eventTimeNs,
-                                                         const HashableDimensionKey& eventKey,
-                                                         const long& value) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
-    if (eventTimeNs < mCurrentBucketStartTimeNs) {
-        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
-             (long long)mCurrentBucketStartTimeNs);
-        return;
-    }
-    mCurrentSlicedBucket[eventKey].raw.push_back(make_pair(value, 0));
-}
-
 void ValueMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
         const LogEvent& event, bool scheduledPull) {
     uint64_t eventTimeNs = event.GetTimestampNs();
-    long value = get_value(event);
+    if (eventTimeNs < mCurrentBucketStartTimeNs) {
+        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+             (long long)mCurrentBucketStartTimeNs);
+        return;
+    }
+
     if (hitGuardRail(eventKey)) {
         return;
     }
+    Interval& interval = mCurrentSlicedBucket[eventKey];
+
+    long value = get_value(event);
+
     if (mPullTagId != -1) {
-        onMatchedLogEventInternal_pull(eventTimeNs, eventKey, value, scheduledPull);
+        if (scheduledPull) {
+            // scheduled pull always sets beginning of current bucket and end
+            // of next bucket
+            if (interval.raw.size() > 0) {
+                interval.raw.back().second = value;
+            } else {
+                interval.raw.push_back(make_pair(value, value));
+            }
+            Interval& nextInterval = mNextSlicedBucket[eventKey];
+            if (nextInterval.raw.size() == 0) {
+                nextInterval.raw.push_back(make_pair(value, 0));
+            } else {
+                nextInterval.raw.front().first = value;
+            }
+        } else {
+            if (mCondition == true) {
+                interval.raw.push_back(make_pair(value, 0));
+            } else {
+                if (interval.raw.size() != 0) {
+                    interval.raw.back().second = value;
+                } else {
+                    interval.tainted = true;
+                    VLOG("Data on condition true missing!");
+                }
+            }
+        }
     } else {
         flushIfNeeded(eventTimeNs);
-        onMatchedLogEventInternal_push(eventTimeNs, eventKey, value);
+        interval.raw.push_back(make_pair(value, 0));
     }
 }
 
-long ValueMetricProducer::get_value(const LogEvent& event) const {
+long ValueMetricProducer::get_value(const LogEvent& event) {
     status_t err = NO_ERROR;
     long val = event.GetLong(mMetric.value_field(), &err);
     if (err == NO_ERROR) {
@@ -359,7 +325,6 @@
 }
 
 void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
-    std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
         VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
              (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
@@ -408,7 +373,6 @@
 }
 
 size_t ValueMetricProducer::byteSize() const {
-    std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
     size_t totalSize = 0;
     for (const auto& pair : mPastBuckets) {
         totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index e87e9da..a024bd8 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -72,16 +72,6 @@
     void startNewProtoOutputStream(long long timestamp) override;
 
 private:
-    void onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs,
-                                        const HashableDimensionKey& eventKey, const long& value,
-                                        bool scheduledPull);
-    void onMatchedLogEventInternal_push(const uint64_t& eventTimeNs,
-                                        const HashableDimensionKey& eventKey, const long& value);
-
-    void SerializeBuckets();
-
-    bool IsConditionMet() const;
-
     const ValueMetric mMetric;
 
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
@@ -92,6 +82,8 @@
                         const int pullTagId, const uint64_t startTimeNs,
                         std::shared_ptr<StatsPullerManager> statsPullerManager);
 
+    Mutex mLock;
+
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
 
@@ -110,7 +102,7 @@
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
 
-    long get_value(const LogEvent& event) const;
+    long get_value(const LogEvent& event);
 
     bool hitGuardRail(const HashableDimensionKey& newKey);
 
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 6c32d3e..db592e2 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -13,11 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 
+#include "guardrail/StatsdStats.h"
 #include "packages/UidMap.h"
 
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
 #include <utils/Errors.h>
 
 using namespace android;
@@ -26,6 +29,11 @@
 namespace os {
 namespace statsd {
 
+UidMap::UidMap() : mBytesUsed(0) {
+}
+UidMap::~UidMap() {
+}
+
 bool UidMap::hasApp(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
@@ -73,6 +81,10 @@
         t->set_version(int(versionCode[j]));
         t->set_uid(uid[j]);
     }
+    mBytesUsed += snapshot->ByteSize();
+    StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
+    StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size());
+    ensureBytesUsedBelowLimit();
 }
 
 void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
@@ -96,6 +108,10 @@
     log->set_app(app);
     log->set_uid(uid);
     log->set_version(versionCode);
+    mBytesUsed += log->ByteSize();
+    StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
+    StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size());
+    ensureBytesUsedBelowLimit();
 
     auto range = mMap.equal_range(int(uid));
     for (auto it = range.first; it != range.second; ++it) {
@@ -103,7 +119,7 @@
             it->second.versionCode = int(versionCode);
             return;
         }
-        ALOGD("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
+        VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
         return;
     }
 
@@ -111,6 +127,28 @@
     mMap.insert(make_pair(uid, AppData(app, int(versionCode))));
 }
 
+void UidMap::ensureBytesUsedBelowLimit() {
+    size_t limit;
+    if (maxBytesOverride <= 0) {
+        limit = StatsdStats::kMaxBytesUsedUidMap;
+    } else {
+        limit = maxBytesOverride;
+    }
+    while (mBytesUsed > limit) {
+        VLOG("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit);
+        if (mOutput.snapshots_size() > 0) {
+            auto snapshots = mOutput.mutable_snapshots();
+            snapshots->erase(snapshots->begin());  // Remove first snapshot.
+            StatsdStats::getInstance().noteUidMapDropped(1, 0);
+        } else if (mOutput.changes_size() > 0) {
+            auto changes = mOutput.mutable_changes();
+            changes->DeleteSubrange(0, 1);
+            StatsdStats::getInstance().noteUidMapDropped(0, 1);
+        }
+        mBytesUsed = mOutput.ByteSize();
+    }
+}
+
 void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
     removeApp(time(nullptr) * NS_PER_SEC, app_16, uid);
 }
@@ -128,6 +166,10 @@
     log->set_timestamp_nanos(timestamp);
     log->set_app(app);
     log->set_uid(uid);
+    mBytesUsed += log->ByteSize();
+    StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
+    StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size());
+    ensureBytesUsedBelowLimit();
 
     auto range = mMap.equal_range(int(uid));
     for (auto it = range.first; it != range.second; ++it) {
@@ -136,7 +178,7 @@
             return;
         }
     }
-    ALOGD("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid);
+    VLOG("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid);
     return;
 }
 
@@ -177,7 +219,6 @@
 
 void UidMap::clearOutput() {
     mOutput.Clear();
-
     // Re-initialize the initial state for the outputs. This results in extra data being uploaded
     // but helps ensure we can re-construct the UID->app name, versionCode mapping in server.
     auto snapshot = mOutput.add_snapshots();
@@ -187,6 +228,12 @@
         t->set_version(it.second.versionCode);
         t->set_uid(it.first);
     }
+
+    // Also update the guardrail trackers.
+    StatsdStats::getInstance().setUidMapChanges(0);
+    StatsdStats::getInstance().setUidMapSnapshots(1);
+    mBytesUsed = snapshot->ByteSize();
+    StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
 }
 
 int64_t UidMap::getMinimumTimestampNs() {
@@ -201,6 +248,10 @@
     return m;
 }
 
+size_t UidMap::getBytesUsed() {
+    return mBytesUsed;
+}
+
 UidMapping UidMap::getOutput(const ConfigKey& key) {
     return getOutput(time(nullptr) * NS_PER_SEC, key);
 }
@@ -236,6 +287,10 @@
             }
         }
     }
+    mBytesUsed = mOutput.ByteSize();  // Compute actual size after potential deletions.
+    StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
+    StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size());
+    StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size());
     return ret;
 }
 
@@ -250,6 +305,23 @@
 
 void UidMap::OnConfigUpdated(const ConfigKey& key) {
     mLastUpdatePerConfigKey[key] = -1;
+
+    // Ensure there is at least one snapshot available since this configuration also needs to know
+    // what all the uid's represent.
+    if (mOutput.snapshots_size() == 0) {
+        sp<IStatsCompanionService> statsCompanion = nullptr;
+        // Get statscompanion service from service manager
+        const sp<IServiceManager> sm(defaultServiceManager());
+        if (sm != nullptr) {
+            const String16 name("statscompanion");
+            statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
+            if (statsCompanion == nullptr) {
+                ALOGW("statscompanion service unavailable!");
+                return;
+            }
+            statsCompanion->triggerUidSnapshot();
+        }
+    }
 }
 
 void UidMap::OnConfigRemoved(const ConfigKey& key) {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 24eb966..d2971c9 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -50,15 +50,16 @@
 // at any given moment. This map must be updated by StatsCompanionService.
 class UidMap : public virtual android::RefBase {
 public:
+    UidMap();
+    ~UidMap();
+
     /*
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
      */
-    // TODO: Add safeguards to call clearOutput if there's too much data already stored.
     void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
                    const vector<String16>& packageName);
 
-    // TODO: Add safeguards to call clearOutput if there's too much data already stored.
     void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
     void removeApp(const String16& packageName, const int32_t& uid);
 
@@ -98,11 +99,13 @@
     // in case we lose a previous upload.
     void clearOutput();
 
+    // Get currently cached value of memory used by UID map.
+    size_t getBytesUsed();
+
 private:
     void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
                    const vector<int32_t>& versionCode, const vector<String16>& packageName);
 
-    // TODO: Add safeguards to call clearOutput if there's too much data already stored.
     void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
                    const int32_t& versionCode);
     void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
@@ -135,8 +138,22 @@
     // Returns the minimum value from mConfigKeys.
     int64_t getMinimumTimestampNs();
 
+    // If our current used bytes is above the limit, then we clear out the earliest snapshot. If
+    // there are no more snapshots, then we clear out the earliest delta. We repeat the deletions
+    // until the memory consumed by mOutput is below the specified limit.
+    void ensureBytesUsedBelowLimit();
+
+    // Override used for testing the max memory allowed by uid map. -1 means we use the value
+    // specified in StatsdStats.h with the rest of the guardrails.
+    size_t maxBytesOverride = -1;
+
+    // Cache the size of mOutput;
+    size_t mBytesUsed;
+
     // Allows unit-test to access private methods.
     FRIEND_TEST(UidMapTest, TestClearingOutput);
+    FRIEND_TEST(UidMapTest, TestMemoryComputed);
+    FRIEND_TEST(UidMapTest, TestMemoryGuardrail);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index e7e1d43..cc8a26d 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -220,4 +220,13 @@
     }
 
     repeated AtomStats atom_stats = 7;
+
+    message UidMapStats {
+        optional int32 snapshots = 1;
+        optional int32 changes = 2;
+        optional int32 bytes_used = 3;
+        optional int32 dropped_snapshots = 4;
+        optional int32 dropped_changes = 5;
+    }
+    optional UidMapStats uidmap_stats = 8;
 }
\ No newline at end of file
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 0c19468..aa194e6 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -15,6 +15,7 @@
 #include "packages/UidMap.h"
 #include "StatsLogProcessor.h"
 #include "config/ConfigKey.h"
+#include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 #include "statslog.h"
 
@@ -35,7 +36,8 @@
 TEST(UidMapTest, TestIsolatedUID) {
     sp<UidMap> m = new UidMap();
     sp<AnomalyMonitor> anomalyMonitor;
-    StatsLogProcessor p(m, anomalyMonitor, nullptr);
+    // Construct the processor with a dummy sendBroadcast function that does nothing.
+    StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {});
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
     addEvent.write(100);  // parent UID
     addEvent.write(101);  // isolated UID
@@ -114,26 +116,34 @@
     versions.push_back(4);
     versions.push_back(5);
     m.updateMap(1, uids, versions, apps);
+    EXPECT_EQ(1, m.mOutput.snapshots_size());
 
     UidMapping results = m.getOutput(2, config1);
     EXPECT_EQ(1, results.snapshots_size());
 
     // It should be cleared now
+    EXPECT_EQ(0, m.mOutput.snapshots_size());
     results = m.getOutput(3, config1);
     EXPECT_EQ(0, results.snapshots_size());
 
     // Now add another configuration.
     m.OnConfigUpdated(config2);
     m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
+    EXPECT_EQ(1, m.mOutput.changes_size());
     results = m.getOutput(6, config1);
     EXPECT_EQ(0, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
+    EXPECT_EQ(1, m.mOutput.changes_size());
 
-    // Now we still haven't been able to delete anything
+    // Add another delta update.
     m.updateApp(7, String16(kApp2.c_str()), 1001, 41);
+    EXPECT_EQ(2, m.mOutput.changes_size());
+
+    // We still can't remove anything.
     results = m.getOutput(8, config1);
     EXPECT_EQ(0, results.snapshots_size());
     EXPECT_EQ(2, results.changes_size());
+    EXPECT_EQ(2, m.mOutput.changes_size());
 
     results = m.getOutput(9, config2);
     EXPECT_EQ(0, results.snapshots_size());
@@ -142,6 +152,66 @@
     EXPECT_EQ(0, m.mOutput.snapshots_size());
     EXPECT_EQ(0, m.mOutput.changes_size());
 }
+
+TEST(UidMapTest, TestMemoryComputed) {
+    UidMap m;
+
+    ConfigKey config1(1, "config1");
+    m.OnConfigUpdated(config1);
+
+    size_t startBytes = m.mBytesUsed;
+    vector<int32_t> uids;
+    vector<int32_t> versions;
+    vector<String16> apps;
+    uids.push_back(1000);
+    apps.push_back(String16(kApp1.c_str()));
+    versions.push_back(1);
+    m.updateMap(1, uids, versions, apps);
+    size_t snapshot_bytes = m.mBytesUsed;
+    EXPECT_TRUE(snapshot_bytes > startBytes);
+
+    m.updateApp(3, String16(kApp1.c_str()), 1000, 40);
+    EXPECT_TRUE(m.mBytesUsed > snapshot_bytes);
+    size_t bytesWithSnapshotChange = m.mBytesUsed;
+
+    m.getOutput(2, config1);
+    EXPECT_TRUE(m.mBytesUsed < bytesWithSnapshotChange);
+    size_t prevBytes = m.mBytesUsed;
+
+    m.getOutput(4, config1);
+    EXPECT_TRUE(m.mBytesUsed < prevBytes);
+}
+
+TEST(UidMapTest, TestMemoryGuardrail) {
+    UidMap m;
+    string buf;
+
+    ConfigKey config1(1, "config1");
+    m.OnConfigUpdated(config1);
+
+    size_t startBytes = m.mBytesUsed;
+    vector<int32_t> uids;
+    vector<int32_t> versions;
+    vector<String16> apps;
+    for (int i = 0; i < 100; i++) {
+        uids.push_back(1);
+        buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i);
+        apps.push_back(String16(buf.c_str()));
+        versions.push_back(1);
+    }
+    m.updateMap(1, uids, versions, apps);
+    EXPECT_EQ(1, m.mOutput.snapshots_size());
+
+    m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2);
+    EXPECT_EQ(1, m.mOutput.snapshots_size());
+    EXPECT_EQ(1, m.mOutput.changes_size());
+
+    // Now force deletion by limiting the memory to hold one delta change.
+    m.maxBytesOverride = 80; // Since the app string alone requires >45 characters.
+    m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4);
+    EXPECT_EQ(0, m.mOutput.snapshots_size());
+    EXPECT_EQ(1, m.mOutput.changes_size());
+}
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index 5b59c21..fe3d86d 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -110,113 +110,7 @@
                 log.getEventMetrics();
         for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) {
             sb.append(getDateStr(event.getTimestampNanos())).append(": ");
-            switch (event.getAtom().getPushedCase()) {
-                case SETTING_CHANGED:
-                    sb.append("SETTING_CHANGED\n");
-                    break;
-                case SYNC_STATE_CHANGED:
-                    sb.append("SYNC_STATE_CHANGED\n");
-                    break;
-                case AUDIO_STATE_CHANGED:
-                    sb.append("AUDIO_STATE_CHANGED\n");
-                    break;
-                case CAMERA_STATE_CHANGED:
-                    sb.append("CAMERA_STATE_CHANGED\n");
-                    break;
-                case ISOLATED_UID_CHANGED:
-                    sb.append("ISOLATED_UID_CHANGED\n");
-                    break;
-                case SCREEN_STATE_CHANGED:
-                    sb.append("SCREEN_STATE_CHANGED\n");
-                    break;
-                case SENSOR_STATE_CHANGED:
-                    sb.append("SENSOR_STATE_CHANGED\n");
-                    break;
-                case BATTERY_LEVEL_CHANGED:
-                    sb.append("BATTERY_LEVEL_CHANGED\n");
-                    break;
-                case PLUGGED_STATE_CHANGED:
-                    sb.append("PLUGGED_STATE_CHANGED\n");
-                    break;
-                case WAKEUP_ALARM_OCCURRED:
-                    sb.append("WAKEUP_ALARM_OCCURRED\n");
-                    break;
-                case BLE_SCAN_STATE_CHANGED:
-                    sb.append("BLE_SCAN_STATE_CHANGED\n");
-                    break;
-                case CHARGING_STATE_CHANGED:
-                    sb.append("CHARGING_STATE_CHANGED\n");
-                    break;
-                case GPS_SCAN_STATE_CHANGED:
-                    sb.append("GPS_SCAN_STATE_CHANGED\n");
-                    break;
-                case KERNEL_WAKEUP_REPORTED:
-                    sb.append("KERNEL_WAKEUP_REPORTED\n");
-                    break;
-                case WAKELOCK_STATE_CHANGED:
-                    sb.append("WAKELOCK_STATE_CHANGED\n");
-                    break;
-                case WIFI_LOCK_STATE_CHANGED:
-                    sb.append("WIFI_LOCK_STATE_CHANGED\n");
-                    break;
-                case WIFI_SCAN_STATE_CHANGED:
-                    sb.append("WIFI_SCAN_STATE_CHANGED\n");
-                    break;
-                case BLE_SCAN_RESULT_RECEIVED:
-                    sb.append("BLE_SCAN_RESULT_RECEIVED\n");
-                    break;
-                case DEVICE_ON_STATUS_CHANGED:
-                    sb.append("DEVICE_ON_STATUS_CHANGED\n");
-                    break;
-                case FLASHLIGHT_STATE_CHANGED:
-                    sb.append("FLASHLIGHT_STATE_CHANGED\n");
-                    break;
-                case SCREEN_BRIGHTNESS_CHANGED:
-                    sb.append("SCREEN_BRIGHTNESS_CHANGED\n");
-                    break;
-                case UID_PROCESS_STATE_CHANGED:
-                    sb.append("UID_PROCESS_STATE_CHANGED\n");
-                    break;
-                case UID_WAKELOCK_STATE_CHANGED:
-                    sb.append("UID_WAKELOCK_STATE_CHANGED\n");
-                    break;
-                case DEVICE_TEMPERATURE_REPORTED:
-                    sb.append("DEVICE_TEMPERATURE_REPORTED\n");
-                    break;
-                case SCHEDULED_JOB_STATE_CHANGED:
-                    sb.append("SCHEDULED_JOB_STATE_CHANGED\n");
-                    break;
-                case MEDIA_CODEC_ACTIVITY_CHANGED:
-                    sb.append("MEDIA_CODEC_ACTIVITY_CHANGED\n");
-                    break;
-                case WIFI_SIGNAL_STRENGTH_CHANGED:
-                    sb.append("WIFI_SIGNAL_STRENGTH_CHANGED\n");
-                    break;
-                case PHONE_SIGNAL_STRENGTH_CHANGED:
-                    sb.append("PHONE_SIGNAL_STRENGTH_CHANGED\n");
-                    break;
-                case DEVICE_IDLE_MODE_STATE_CHANGED:
-                    sb.append("DEVICE_IDLE_MODE_STATE_CHANGED\n");
-                    break;
-                case BATTERY_SAVER_MODE_STATE_CHANGED:
-                    sb.append("BATTERY_SAVER_MODE_STATE_CHANGED\n");
-                    break;
-                case PROCESS_LIFE_CYCLE_STATE_CHANGED:
-                    sb.append("PROCESS_LIFE_CYCLE_STATE_CHANGED\n");
-                    break;
-                case ACTIVITY_FOREGROUND_STATE_CHANGED:
-                    sb.append("ACTIVITY_FOREGROUND_STATE_CHANGED\n");
-                    break;
-                case BLE_UNOPTIMIZED_SCAN_STATE_CHANGED:
-                    sb.append("BLE_UNOPTIMIZED_SCAN_STATE_CHANGED\n");
-                    break;
-                case LONG_PARTIAL_WAKELOCK_STATE_CHANGED:
-                    sb.append("LONG_PARTIAL_WAKELOCK_STATE_CHANGED\n");
-                    break;
-                case PUSHED_NOT_SET:
-                    sb.append("PUSHED_NOT_SET\n");
-                    break;
-            }
+            sb.append(event.getAtom().getPushedCase().toString()).append("\n");
         }
     }
 
diff --git a/core/java/Android.bp b/core/java/Android.bp
index d8c7929..b43cf27 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -4,16 +4,16 @@
 }
 
 // only used by key_store_service
-cc_library_static {
+cc_library_shared {
     name: "libkeystore_aidl",
     srcs: ["android/security/IKeystoreService.aidl"],
     aidl: {
         export_aidl_headers: true,
-        include_dirs: ["frameworks/base/core/java/"],
+        include_dirs: [
+            "frameworks/base/core/java/",
+            "system/security/keystore/",
+        ],
     },
-    header_libs: [
-        "libkeystore_headers",
-    ],
     shared_libs: [
         "libbinder",
         "libcutils",
@@ -22,7 +22,12 @@
         "libhidltransport",
         "libhwbinder",
         "liblog",
+        "libkeystore_parcelables",
         "libselinux",
         "libutils",
     ],
+    export_shared_lib_headers: [
+        "libbinder",
+        "libkeystore_parcelables",
+    ],
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 06a9b06..e0d60cd 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
 
 package android.accessibilityservice;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -47,8 +49,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -554,7 +554,7 @@
     }
 
     /**
-     * Updates the properties that an AccessibilitySerivice can change dynamically.
+     * Updates the properties that an AccessibilityService can change dynamically.
      *
      * @param other The info from which to update the properties.
      *
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 6ea0825..8c47598 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -34,8 +34,6 @@
 
 import libcore.io.IoUtils;
 
-import com.google.android.collect.Maps;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.BufferedInputStream;
@@ -139,7 +137,7 @@
             Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
         }
 
-        Map map = null;
+        Map<String, Object> map = null;
         StructStat stat = null;
         try {
             stat = Os.stat(mFile.getPath());
@@ -148,7 +146,7 @@
                 try {
                     str = new BufferedInputStream(
                             new FileInputStream(mFile), 16*1024);
-                    map = XmlUtils.readMapXml(str);
+                    map = (Map<String, Object>) XmlUtils.readMapXml(str);
                 } catch (Exception e) {
                     Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
                 } finally {
@@ -214,12 +212,14 @@
         }
     }
 
+    @Override
     public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
         synchronized(mLock) {
             mListeners.put(listener, CONTENT);
         }
     }
 
+    @Override
     public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
         synchronized(mLock) {
             mListeners.remove(listener);
@@ -241,6 +241,7 @@
         }
     }
 
+    @Override
     public Map<String, ?> getAll() {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -249,6 +250,7 @@
         }
     }
 
+    @Override
     @Nullable
     public String getString(String key, @Nullable String defValue) {
         synchronized (mLock) {
@@ -258,6 +260,7 @@
         }
     }
 
+    @Override
     @Nullable
     public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
         synchronized (mLock) {
@@ -267,6 +270,7 @@
         }
     }
 
+    @Override
     public int getInt(String key, int defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -274,6 +278,7 @@
             return v != null ? v : defValue;
         }
     }
+    @Override
     public long getLong(String key, long defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -281,6 +286,7 @@
             return v != null ? v : defValue;
         }
     }
+    @Override
     public float getFloat(String key, float defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -288,6 +294,7 @@
             return v != null ? v : defValue;
         }
     }
+    @Override
     public boolean getBoolean(String key, boolean defValue) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -296,6 +303,7 @@
         }
     }
 
+    @Override
     public boolean contains(String key) {
         synchronized (mLock) {
             awaitLoadedLocked();
@@ -303,6 +311,7 @@
         }
     }
 
+    @Override
     public Editor edit() {
         // TODO: remove the need to call awaitLoadedLocked() when
         // requesting an editor.  will require some work on the
@@ -347,71 +356,81 @@
     }
 
     public final class EditorImpl implements Editor {
-        private final Object mLock = new Object();
+        private final Object mEditorLock = new Object();
 
-        @GuardedBy("mLock")
-        private final Map<String, Object> mModified = Maps.newHashMap();
+        @GuardedBy("mEditorLock")
+        private final Map<String, Object> mModified = new HashMap<>();
 
-        @GuardedBy("mLock")
+        @GuardedBy("mEditorLock")
         private boolean mClear = false;
 
+        @Override
         public Editor putString(String key, @Nullable String value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putStringSet(String key, @Nullable Set<String> values) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key,
                         (values == null) ? null : new HashSet<String>(values));
                 return this;
             }
         }
+        @Override
         public Editor putInt(String key, int value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putLong(String key, long value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putFloat(String key, float value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
+        @Override
         public Editor putBoolean(String key, boolean value) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
 
+        @Override
         public Editor remove(String key) {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mModified.put(key, this);
                 return this;
             }
         }
 
+        @Override
         public Editor clear() {
-            synchronized (mLock) {
+            synchronized (mEditorLock) {
                 mClear = true;
                 return this;
             }
         }
 
+        @Override
         public void apply() {
             final long startTime = System.currentTimeMillis();
 
             final MemoryCommitResult mcr = commitToMemory();
             final Runnable awaitCommit = new Runnable() {
+                    @Override
                     public void run() {
                         try {
                             mcr.writtenToDiskLatch.await();
@@ -429,6 +448,7 @@
             QueuedWork.addFinisher(awaitCommit);
 
             Runnable postWriteRunnable = new Runnable() {
+                    @Override
                     public void run() {
                         awaitCommit.run();
                         QueuedWork.removeFinisher(awaitCommit);
@@ -471,13 +491,13 @@
                     listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
                 }
 
-                synchronized (mLock) {
+                synchronized (mEditorLock) {
                     boolean changesMade = false;
 
                     if (mClear) {
-                        if (!mMap.isEmpty()) {
+                        if (!mapToWriteToDisk.isEmpty()) {
                             changesMade = true;
-                            mMap.clear();
+                            mapToWriteToDisk.clear();
                         }
                         mClear = false;
                     }
@@ -489,18 +509,18 @@
                         // setting a value to "null" for a given key is specified to be
                         // equivalent to calling remove on that key.
                         if (v == this || v == null) {
-                            if (!mMap.containsKey(k)) {
+                            if (!mapToWriteToDisk.containsKey(k)) {
                                 continue;
                             }
-                            mMap.remove(k);
+                            mapToWriteToDisk.remove(k);
                         } else {
-                            if (mMap.containsKey(k)) {
-                                Object existingValue = mMap.get(k);
+                            if (mapToWriteToDisk.containsKey(k)) {
+                                Object existingValue = mapToWriteToDisk.get(k);
                                 if (existingValue != null && existingValue.equals(v)) {
                                     continue;
                                 }
                             }
-                            mMap.put(k, v);
+                            mapToWriteToDisk.put(k, v);
                         }
 
                         changesMade = true;
@@ -522,6 +542,7 @@
                     mapToWriteToDisk);
         }
 
+        @Override
         public boolean commit() {
             long startTime = 0;
 
@@ -564,11 +585,7 @@
                 }
             } else {
                 // Run this function on the main thread.
-                ActivityThread.sMainThreadHandler.post(new Runnable() {
-                        public void run() {
-                            notifyListeners(mcr);
-                        }
-                    });
+                ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr));
             }
         }
     }
@@ -594,6 +611,7 @@
         final boolean isFromSyncCommit = (postWriteRunnable == null);
 
         final Runnable writeToDiskRunnable = new Runnable() {
+                @Override
                 public void run() {
                     synchronized (mWritingToDiskLock) {
                         writeToFile(mcr, isFromSyncCommit);
@@ -646,7 +664,7 @@
         return str;
     }
 
-    // Note: must hold mWritingToDiskLock
+    @GuardedBy("mWritingToDiskLock")
     private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
         long startTime = 0;
         long existsTime = 0;
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 8d81199..cdeee35 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -216,7 +216,7 @@
      * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
      */
     public Slice getSlice() {
-        if (getFormat() == FORMAT_ACTION) {
+        if (FORMAT_ACTION.equals(getFormat())) {
             return ((Pair<PendingIntent, Slice>) mObj).second;
         }
         return (Slice) mObj;
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index b7e353a..52527ed 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -16,43 +16,48 @@
 package android.hardware.location;
 
 import android.annotation.RequiresPermission;
-import android.os.Handler;
+import android.os.RemoteException;
+
+import dalvik.system.CloseGuard;
 
 import java.io.Closeable;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A class describing a client of the Context Hub Service.
  *
- * Clients can send messages to nanoapps at a Context Hub through this object.
+ * Clients can send messages to nanoapps at a Context Hub through this object. The APIs supported
+ * by this object are thread-safe and can be used without external synchronization.
  *
  * @hide
  */
 public class ContextHubClient implements Closeable {
     /*
-     * The ContextHubClient interface associated with this client.
+     * The proxy to the client interface at the service.
      */
-    // TODO: Implement this interface and associate with ContextHubClient object
-    // private final IContextHubClient mClientInterface;
+    private final IContextHubClient mClientProxy;
 
     /*
-     * The listening callback associated with this client.
+     * The callback interface associated with this client.
      */
-    private ContextHubClientCallback mCallback;
+    private final IContextHubClientCallback mCallbackInterface;
 
     /*
      * The Context Hub that this client is attached to.
      */
-    private ContextHubInfo mAttachedHub;
+    private final ContextHubInfo mAttachedHub;
 
-    /*
-     * The handler to invoke mCallback.
-     */
-    private Handler mCallbackHandler;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
-    ContextHubClient(ContextHubClientCallback callback, Handler handler, ContextHubInfo hubInfo) {
-        mCallback = callback;
-        mCallbackHandler = handler;
+    private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+    /* package */ ContextHubClient(
+            IContextHubClient clientProxy, IContextHubClientCallback callback,
+            ContextHubInfo hubInfo) {
+        mClientProxy = clientProxy;
+        mCallbackInterface = callback;
         mAttachedHub = hubInfo;
+        mCloseGuard.open("close");
     }
 
     /**
@@ -71,7 +76,14 @@
      * All futures messages targeted for this client are dropped at the service.
      */
     public void close() {
-        throw new UnsupportedOperationException("TODO: Implement this");
+        if (!mIsClosed.getAndSet(true)) {
+            mCloseGuard.close();
+            try {
+                mClientProxy.close();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
     }
 
     /**
@@ -90,6 +102,22 @@
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @ContextHubTransaction.Result
     public int sendMessageToNanoApp(NanoAppMessage message) {
-        throw new UnsupportedOperationException("TODO: Implement this");
+        try {
+            return mClientProxy.sendMessageToNanoApp(message);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            close();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 2411727..b31c7bc 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -456,6 +456,54 @@
     }
 
     /**
+     * Creates an interface to the ContextHubClient to send down to the service.
+     *
+     * @param callback the callback to invoke at the client process
+     * @param handler the handler to post callbacks for this client
+     *
+     * @return the callback interface
+     */
+    private IContextHubClientCallback createClientCallback(
+            ContextHubClientCallback callback, Handler handler) {
+        return new IContextHubClientCallback.Stub() {
+            @Override
+            public void onMessageFromNanoApp(NanoAppMessage message) {
+                handler.post(() -> callback.onMessageFromNanoApp(message));
+            }
+
+            @Override
+            public void onHubReset() {
+                handler.post(() -> callback.onHubReset());
+            }
+
+            @Override
+            public void onNanoAppAborted(long nanoAppId, int abortCode) {
+                handler.post(() -> callback.onNanoAppAborted(nanoAppId, abortCode));
+            }
+
+            @Override
+            public void onNanoAppLoaded(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppLoaded(nanoAppId));
+            }
+
+            @Override
+            public void onNanoAppUnloaded(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppUnloaded(nanoAppId));
+            }
+
+            @Override
+            public void onNanoAppEnabled(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppEnabled(nanoAppId));
+            }
+
+            @Override
+            public void onNanoAppDisabled(long nanoAppId) {
+                handler.post(() -> callback.onNanoAppDisabled(nanoAppId));
+            }
+        };
+    }
+
+    /**
      * Creates and registers a client and its callback with the Context Hub Service.
      *
      * A client is registered with the Context Hub Service for a specified Context Hub. When the
@@ -463,19 +511,37 @@
      * {@link ContextHubClient} object, and receive notifications through the provided callback.
      *
      * @param callback the notification callback to register
-     * @param hubInfo the hub to attach this client to
-     * @param handler the handler to invoke the callback, if null uses the main thread's Looper
-     *
+     * @param hubInfo  the hub to attach this client to
+     * @param handler  the handler to invoke the callback, if null uses the main thread's Looper
      * @return the registered client object
      *
-     * @see ContextHubClientCallback
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws NullPointerException     if callback or hubInfo is null
      *
      * @hide
+     * @see ContextHubClientCallback
      */
     public ContextHubClient createClient(
             ContextHubClientCallback callback, ContextHubInfo hubInfo, @Nullable Handler handler) {
-        throw new UnsupportedOperationException(
-                "TODO: Implement this, and throw an exception on error");
+        if (callback == null) {
+            throw new NullPointerException("Callback cannot be null");
+        }
+        if (hubInfo == null) {
+            throw new NullPointerException("Hub info cannot be null");
+        }
+
+        Handler realHandler = (handler == null) ? new Handler(mMainLooper) : handler;
+        IContextHubClientCallback clientInterface = createClientCallback(callback, realHandler);
+
+        IContextHubClient client;
+        try {
+            client = mService.createClient(clientInterface, hubInfo.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return new ContextHubClient(client, clientInterface, hubInfo);
     }
 
     /**
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
new file mode 100644
index 0000000..d81126a
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * @hide
+ */
+interface IContextHubClient {
+
+    // Sends a message to a nanoapp
+    int sendMessageToNanoApp(in NanoAppMessage message);
+
+    // Closes the connection with the Context Hub
+    void close();
+}
diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl
new file mode 100644
index 0000000..1c76bcb
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * An interface used by the Context Hub Service to invoke callbacks for lifecycle notifications of a
+ * Context Hub and nanoapps, as well as for nanoapp messaging.
+ *
+ * @hide
+ */
+oneway interface IContextHubClientCallback {
+
+    // Callback invoked when receiving a message from a nanoapp.
+    void onMessageFromNanoApp(in NanoAppMessage message);
+
+    // Callback invoked when the attached Context Hub has reset.
+    void onHubReset();
+
+    // Callback invoked when a nanoapp aborts at the attached Context Hub.
+    void onNanoAppAborted(long nanoAppId, int abortCode);
+
+    // Callback invoked when a nanoapp is loaded at the attached Context Hub.
+    void onNanoAppLoaded(long nanoAppId);
+
+    // Callback invoked when a nanoapp is unloaded from the attached Context Hub.
+    void onNanoAppUnloaded(long nanoAppId);
+
+    // Callback invoked when a nanoapp is enabled at the attached Context Hub.
+    void onNanoAppEnabled(long nanoAppId);
+
+    // Callback invoked when a nanoapp is disabled at the attached Context Hub.
+    void onNanoAppDisabled(long nanoAppId);
+}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index ff8c1d0..1bb7c8f 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -17,12 +17,14 @@
 package android.hardware.location;
 
 // Declare any non-default types here with import statements
-import android.hardware.location.ContextHubMessage;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubMessage;
 import android.hardware.location.NanoApp;
-import android.hardware.location.NanoAppInstanceInfo;
 import android.hardware.location.NanoAppFilter;
+import android.hardware.location.NanoAppInstanceInfo;
 import android.hardware.location.IContextHubCallback;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
 
 /**
  * @hide
@@ -52,4 +54,7 @@
 
     // send a message to a nanoApp
     int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg);
+
+    // Creates a client to send and receive messages
+    IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
 }
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 13b9206..7836cd0 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -21,18 +21,16 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.Paint.Align;
 import android.graphics.Region.Op;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.Keyboard.Key;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.GestureDetector;
@@ -986,6 +984,9 @@
 
     private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code) {
         if (mAccessibilityManager.isEnabled()) {
+            if (!mAccessibilityManager.isObservedEventType(eventType)) {
+                return;
+            }
             AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
             onInitializeAccessibilityEvent(event);
             final String text;
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 3314f60..1d2a408 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -56,4 +56,7 @@
 
     /** Send a broadcast to the specified pkg and class that it should getData now. */
     oneway void sendBroadcast(String pkg, String cls);
+
+    /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
+    oneway void triggerUidSnapshot();
 }
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index f903012..2169457 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -158,7 +158,6 @@
      * <p>
      * Dismiss all currently expired timers. If there are no expired timers, then this is a no-op.
      * </p>
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_DISMISS_TIMER = "android.intent.action.DISMISS_TIMER";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 729c0ff..1e0948a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10456,6 +10456,15 @@
                 "storage_settings_clobber_threshold";
 
         /**
+         * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF}
+         * temporarily for all users.
+         *
+         * @hide
+         */
+        public static final String LOCATION_GLOBAL_KILL_SWITCH =
+                "location_global_kill_switch";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index cd233b8..df0842f 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -105,6 +105,13 @@
     public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
             "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
 
+    /**
+     * Intent extra set for resolution requests containing a boolean indicating whether to ask the
+     * user to retry another confirmation code.
+     */
+    public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED =
+            "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
+
     /** Result code for a successful operation. */
     public static final int RESULT_OK = 0;
     /** Result code indicating that an active SIM must be deactivated to perform the operation. */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cf05910..3d01ec2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -55,6 +55,8 @@
     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 void nativeCaptureLayers(IBinder layerHandleToken, Surface consumer,
+            Rect sourceCrop, float frameScale);
     private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
             Rect sourceCrop, float frameScale);
 
@@ -1177,14 +1179,22 @@
      * Captures a layer and its children into the provided {@link Surface}.
      *
      * @param layerHandleToken The root layer to capture.
+     * @param consumer         The {@link Surface} to capture the layer into.
      * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
      *                         Rect()' or null if no cropping is desired.
      * @param frameScale       The desired scale of the returned buffer; the raw
      *                         screen will be scaled up/down.
-     *
-     * @return Returns a GraphicBuffer that contains the layer capture.
      */
-    public static GraphicBuffer captureLayers(IBinder layerHandleToken, Rect sourceCrop,
+    public static void captureLayers(IBinder layerHandleToken, Surface consumer, Rect sourceCrop,
+            float frameScale) {
+        nativeCaptureLayers(layerHandleToken, consumer, sourceCrop, frameScale);
+    }
+
+    /**
+     * Same as {@link #captureLayers(IBinder, Surface, Rect, float)} except this
+     * captures to a {@link GraphicBuffer} instead of a {@link Surface}.
+     */
+    public static GraphicBuffer captureLayersToBuffer(IBinder layerHandleToken, Rect sourceCrop,
             float frameScale) {
         return nativeCaptureLayers(layerHandleToken, sourceCrop, frameScale);
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 345e300..0525ab1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7208,7 +7208,8 @@
      * @param text The announcement text.
      */
     public void announceForAccessibility(CharSequence text) {
-        if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
+        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                AccessibilityEvent.TYPE_ANNOUNCEMENT) && mParent != null) {
             AccessibilityEvent event = AccessibilityEvent.obtain(
                     AccessibilityEvent.TYPE_ANNOUNCEMENT);
             onInitializeAccessibilityEvent(event);
@@ -10967,7 +10968,8 @@
         if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
             invalidate();
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
                 event.setAction(action);
@@ -11792,7 +11794,8 @@
 
     private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
             int fromIndex, int toIndex) {
-        if (mParent == null) {
+        if (mParent == null || !AccessibilityManager.getInstance(mContext).isObservedEventType(
+                AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY)) {
             return;
         }
         AccessibilityEvent event = AccessibilityEvent.obtain(
@@ -26182,7 +26185,8 @@
 
         @Override
         public void run() {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                    AccessibilityEvent.TYPE_VIEW_SCROLLED)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_SCROLLED);
                 event.setScrollDeltaX(mDeltaX);
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 5adea66..1d19a9f 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -23,6 +24,8 @@
 
 import com.android.internal.util.BitUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -709,6 +712,38 @@
      */
     public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
 
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_VIEW_CLICKED,
+            TYPE_VIEW_LONG_CLICKED,
+            TYPE_VIEW_SELECTED,
+            TYPE_VIEW_FOCUSED,
+            TYPE_VIEW_TEXT_CHANGED,
+            TYPE_WINDOW_STATE_CHANGED,
+            TYPE_NOTIFICATION_STATE_CHANGED,
+            TYPE_VIEW_HOVER_ENTER,
+            TYPE_VIEW_HOVER_EXIT,
+            TYPE_TOUCH_EXPLORATION_GESTURE_START,
+            TYPE_TOUCH_EXPLORATION_GESTURE_END,
+            TYPE_WINDOW_CONTENT_CHANGED,
+            TYPE_VIEW_SCROLLED,
+            TYPE_VIEW_TEXT_SELECTION_CHANGED,
+            TYPE_ANNOUNCEMENT,
+            TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+            TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
+            TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+            TYPE_GESTURE_DETECTION_START,
+            TYPE_GESTURE_DETECTION_END,
+            TYPE_TOUCH_INTERACTION_START,
+            TYPE_TOUCH_INTERACTION_END,
+            TYPE_WINDOWS_CHANGED,
+            TYPE_VIEW_CONTEXT_CLICKED,
+            TYPE_ASSIST_READING_CONTEXT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType {}
+
     /**
      * Mask for {@link AccessibilityEvent} all types.
      *
@@ -741,7 +776,7 @@
     private static final SynchronizedPool<AccessibilityEvent> sPool =
             new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
 
-    private int mEventType;
+    private @EventType int mEventType;
     private CharSequence mPackageName;
     private long mEventTime;
     int mMovementGranularity;
@@ -833,7 +868,7 @@
      *
      * @return The event type.
      */
-    public int getEventType() {
+    public @EventType int getEventType() {
         return mEventType;
     }
 
@@ -890,7 +925,7 @@
      *
      * @throws IllegalStateException If called from an AccessibilityService.
      */
-    public void setEventType(int eventType) {
+    public void setEventType(@EventType int eventType) {
         enforceNotSealed();
         mEventType = eventType;
     }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 35f6acb..0375635 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -24,6 +24,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -187,6 +188,7 @@
      *
      * @hide
      */
+    @TestApi
     public interface AccessibilityServicesStateChangeListener {
 
         /**
@@ -452,6 +454,18 @@
     }
 
     /**
+     * Returns whether there are observers registered for this event type. If
+     * this method returns false you shuold not generate events of this type
+     * to conserve resources.
+     *
+     * @param type The event type.
+     * @return Whether the event is being observed.
+     */
+    public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+        return mIsEnabled && (mRelevantEventTypes & type) != 0;
+    }
+
+    /**
      * Requests feedback interruption from all accessibility services.
      */
     public void interrupt() {
@@ -683,6 +697,7 @@
      *                for a callback on the process's main handler.
      * @hide
      */
+    @TestApi
     public void addAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
         synchronized (mLock) {
@@ -698,6 +713,7 @@
      *
      * @hide
      */
+    @TestApi
     public void removeAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener) {
         // Final CopyOnWriteArrayList - no lock needed.
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 2779aa2..f675c35 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -31,6 +31,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Information for generating a widget to handle classified text.
@@ -42,7 +43,7 @@
  *
  * <pre>{@code
  *   // Called preferably outside the UiThread.
- *   TextClassification classification = textClassifier.classifyText(allText, 10, 25, null);
+ *   TextClassification classification = textClassifier.classifyText(allText, 10, 25);
  *
  *   // Called on the UiThread.
  *   Button button = new Button(context);
@@ -55,7 +56,7 @@
  *
  * <pre>{@code
  *   // Called preferably outside the UiThread.
- *   final TextClassification classification = textClassifier.classifyText(allText, 10, 25, null);
+ *   final TextClassification classification = textClassifier.classifyText(allText, 10, 25);
  *
  *   // Called on the UiThread.
  *   view.startActionMode(new ActionMode.Callback() {
@@ -281,8 +282,8 @@
 
     @Override
     public String toString() {
-        return String.format("TextClassification {"
-                        + "text=%s, entities=%s, labels=%s, intents=%s}",
+        return String.format(Locale.US,
+                "TextClassification {text=%s, entities=%s, labels=%s, intents=%s}",
                 mText, mEntityConfidence, mLabels, mIntents);
     }
 
@@ -421,7 +422,7 @@
         }
 
         /**
-         * Ensures that we have at we have storage for the default action.
+         * Ensures that we have storage for the default action.
          */
         private void ensureDefaultActionAvailable() {
             if (mIntents.isEmpty()) mIntents.add(null);
@@ -441,7 +442,7 @@
     }
 
     /**
-     * TextClassification optional input parameters.
+     * Optional input parameters for generating TextClassification.
      */
     public static final class Options {
 
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index aeb8489..5aaa5ad 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -23,6 +23,8 @@
 import android.annotation.WorkerThread;
 import android.os.LocaleList;
 
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -37,8 +39,7 @@
     /** @hide */
     String DEFAULT_LOG_TAG = "TextClassifierImpl";
 
-    /** @hide */
-    String TYPE_UNKNOWN = "";  // TODO: Make this public API.
+    String TYPE_UNKNOWN = "";
     String TYPE_OTHER = "other";
     String TYPE_EMAIL = "email";
     String TYPE_PHONE = "phone";
@@ -70,6 +71,8 @@
      *
      * @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
      *      selectionEndIndex is greater than text.length() or not greater than selectionStartIndex
+     *
+     * @see #suggestSelection(CharSequence, int, int)
      */
     @WorkerThread
     @NonNull
@@ -78,13 +81,46 @@
             @IntRange(from = 0) int selectionStartIndex,
             @IntRange(from = 0) int selectionEndIndex,
             @Nullable TextSelection.Options options) {
+        Utils.validateInput(text, selectionStartIndex, selectionEndIndex);
         return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
     }
 
     /**
+     * Returns suggested text selection start and end indices, recognized entity types, and their
+     * associated confidence scores. The entity types are ordered from highest to lowest scoring.
+     *
+     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
+     * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
+     * calls this method, a stack overflow error will happen.
+     *
+     * @param text text providing context for the selected text (which is specified
+     *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
+     * @param selectionStartIndex start index of the selected part of text
+     * @param selectionEndIndex end index of the selected part of text
+     *
+     * @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
+     *      selectionEndIndex is greater than text.length() or not greater than selectionStartIndex
+     *
      * @see #suggestSelection(CharSequence, int, int, TextSelection.Options)
      */
-    // TODO: Consider deprecating (b/68846316)
+    @WorkerThread
+    @NonNull
+    default TextSelection suggestSelection(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int selectionStartIndex,
+            @IntRange(from = 0) int selectionEndIndex) {
+        return suggestSelection(text, selectionStartIndex, selectionEndIndex,
+                (TextSelection.Options) null);
+    }
+
+    /**
+     * See {@link #suggestSelection(CharSequence, int, int)} or
+     * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}.
+     *
+     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
+     * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
+     * calls this method, a stack overflow error will happen.
+     */
     @WorkerThread
     @NonNull
     default TextSelection suggestSelection(
@@ -92,7 +128,10 @@
             @IntRange(from = 0) int selectionStartIndex,
             @IntRange(from = 0) int selectionEndIndex,
             @Nullable LocaleList defaultLocales) {
-        return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
+        final TextSelection.Options options = (defaultLocales != null)
+                ? new TextSelection.Options().setDefaultLocales(defaultLocales)
+                : null;
+        return suggestSelection(text, selectionStartIndex, selectionEndIndex, options);
     }
 
     /**
@@ -107,6 +146,8 @@
      *
      * @throws IllegalArgumentException if text is null; startIndex is negative;
      *      endIndex is greater than text.length() or not greater than startIndex
+     *
+     * @see #classifyText(CharSequence, int, int)
      */
     @WorkerThread
     @NonNull
@@ -115,13 +156,45 @@
             @IntRange(from = 0) int startIndex,
             @IntRange(from = 0) int endIndex,
             @Nullable TextClassification.Options options) {
+        Utils.validateInput(text, startIndex, endIndex);
         return TextClassification.EMPTY;
     }
 
     /**
+     * Classifies the specified text and returns a {@link TextClassification} object that can be
+     * used to generate a widget for handling the classified text.
+     *
+     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
+     * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
+     * calls this method, a stack overflow error will happen.
+     *
+     * @param text text providing context for the text to classify (which is specified
+     *      by the sub sequence starting at startIndex and ending at endIndex)
+     * @param startIndex start index of the text to classify
+     * @param endIndex end index of the text to classify
+     *
+     * @throws IllegalArgumentException if text is null; startIndex is negative;
+     *      endIndex is greater than text.length() or not greater than startIndex
+     *
      * @see #classifyText(CharSequence, int, int, TextClassification.Options)
      */
-    // TODO: Consider deprecating (b/68846316)
+    @WorkerThread
+    @NonNull
+    default TextClassification classifyText(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int startIndex,
+            @IntRange(from = 0) int endIndex) {
+        return classifyText(text, startIndex, endIndex, (TextClassification.Options) null);
+    }
+
+    /**
+     * See {@link #classifyText(CharSequence, int, int, TextClassification.Options)} or
+     * {@link #classifyText(CharSequence, int, int)}.
+     *
+     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
+     * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
+     * calls this method, a stack overflow error will happen.
+     */
     @WorkerThread
     @NonNull
     default TextClassification classifyText(
@@ -129,7 +202,10 @@
             @IntRange(from = 0) int startIndex,
             @IntRange(from = 0) int endIndex,
             @Nullable LocaleList defaultLocales) {
-        return TextClassification.EMPTY;
+        final TextClassification.Options options = (defaultLocales != null)
+                ? new TextClassification.Options().setDefaultLocales(defaultLocales)
+                : null;
+        return classifyText(text, startIndex, endIndex, options);
     }
 
     /**
@@ -137,17 +213,39 @@
      * information.
      *
      * @param text the text to generate annotations for
-     * @param options configuration for link generation. If null, defaults will be used.
+     * @param options configuration for link generation
      *
      * @throws IllegalArgumentException if text is null
+     *
+     * @see #generateLinks(CharSequence)
      */
     @WorkerThread
     default TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
+        Utils.validateInput(text);
         return new TextLinks.Builder(text.toString()).build();
     }
 
     /**
+     * Returns a {@link TextLinks} that may be applied to the text to annotate it with links
+     * information.
+     *
+     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
+     * {@link #generateLinks(CharSequence, TextLinks.Options)}. If that method calls this method,
+     * a stack overflow error will happen.
+     *
+     * @param text the text to generate annotations for
+     *
+     * @throws IllegalArgumentException if text is null
+     *
+     * @see #generateLinks(CharSequence, TextLinks.Options)
+     */
+    @WorkerThread
+    default TextLinks generateLinks(@NonNull CharSequence text) {
+        return generateLinks(text, null);
+    }
+
+    /**
      * Logs a TextClassifier event.
      *
      * @param source the text classifier used to generate this event
@@ -164,4 +262,38 @@
     default TextClassifierConstants getSettings() {
         return TextClassifierConstants.DEFAULT;
     }
+
+
+    /**
+     * Utility functions for TextClassifier methods.
+     *
+     * <ul>
+     *  <li>Provides validation of input parameters to TextClassifier methods
+     * </ul>
+     *
+     * Intended to be used only in this package.
+     * @hide
+     */
+    final class Utils {
+
+        /**
+         * @throws IllegalArgumentException if text is null; startIndex is negative;
+         *      endIndex is greater than text.length() or is not greater than startIndex;
+         *      options is null
+         */
+        static void validateInput(
+                @NonNull CharSequence text, int startIndex, int endIndex) {
+            Preconditions.checkArgument(text != null);
+            Preconditions.checkArgument(startIndex >= 0);
+            Preconditions.checkArgument(endIndex <= text.length());
+            Preconditions.checkArgument(endIndex > startIndex);
+        }
+
+        /**
+         * @throws IllegalArgumentException if text is null or options is null
+         */
+        static void validateInput(@NonNull CharSequence text) {
+            Preconditions.checkArgument(text != null);
+        }
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 2ad6e02..df5e35f 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -90,8 +90,8 @@
     @Override
     public TextSelection suggestSelection(
             @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
-            @Nullable TextSelection.Options options) {
-        validateInput(text, selectionStartIndex, selectionEndIndex);
+            @NonNull TextSelection.Options options) {
+        Utils.validateInput(text, selectionStartIndex, selectionEndIndex);
         try {
             if (text.length() > 0) {
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
@@ -141,18 +141,10 @@
     }
 
     @Override
-    public TextSelection suggestSelection(
-            @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
-            @Nullable LocaleList defaultLocales) {
-        return suggestSelection(text, selectionStartIndex, selectionEndIndex,
-                new TextSelection.Options().setDefaultLocales(defaultLocales));
-    }
-
-    @Override
     public TextClassification classifyText(
             @NonNull CharSequence text, int startIndex, int endIndex,
-            @Nullable TextClassification.Options options) {
-        validateInput(text, startIndex, endIndex);
+            @NonNull TextClassification.Options options) {
+        Utils.validateInput(text, startIndex, endIndex);
         try {
             if (text.length() > 0) {
                 final String string = text.toString();
@@ -176,17 +168,9 @@
     }
 
     @Override
-    public TextClassification classifyText(
-            @NonNull CharSequence text, int startIndex, int endIndex,
-            @Nullable LocaleList defaultLocales) {
-        return classifyText(text, startIndex, endIndex,
-                new TextClassification.Options().setDefaultLocales(defaultLocales));
-    }
-
-    @Override
     public TextLinks generateLinks(
-            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        Preconditions.checkNotNull(text);
+            @NonNull CharSequence text, @NonNull TextLinks.Options options) {
+        Utils.validateInput(text);
         final String textString = text.toString();
         final TextLinks.Builder builder = new TextLinks.Builder(textString);
         try {
@@ -486,17 +470,6 @@
     }
 
     /**
-     * @throws IllegalArgumentException if text is null; startIndex is negative;
-     *      endIndex is greater than text.length() or is not greater than startIndex
-     */
-    private static void validateInput(@NonNull CharSequence text, int startIndex, int endIndex) {
-        Preconditions.checkArgument(text != null);
-        Preconditions.checkArgument(startIndex >= 0);
-        Preconditions.checkArgument(endIndex <= text.length());
-        Preconditions.checkArgument(endIndex > startIndex);
-    }
-
-    /**
      * Creates intents based on the classification type.
      */
     private static final class IntentFactory {
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index f3cc827..76748d2 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -161,39 +161,28 @@
      * Optional input parameters for generating TextLinks.
      */
     public static final class Options {
-        private final LocaleList mLocaleList;
 
-        private Options(LocaleList localeList) {
-            this.mLocaleList = localeList;
+        private LocaleList mDefaultLocales;
+
+        /**
+         * @param defaultLocales ordered list of locale preferences that may be used to disambiguate
+         *      the provided text. If no locale preferences exist, set this to null or an empty
+         *      locale list.
+         */
+        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
+            mDefaultLocales = defaultLocales;
+            return this;
         }
 
         /**
-         * Builder to construct Options.
+         * @return ordered list of locale preferences that can be used to disambiguate
+         *      the provided text.
          */
-        public static final class Builder {
-            private LocaleList mLocaleList;
-
-            /**
-             * Sets the LocaleList to use.
-             *
-             * @return this Builder.
-             */
-            public Builder setLocaleList(@Nullable LocaleList localeList) {
-                this.mLocaleList = localeList;
-                return this;
-            }
-
-            /**
-             * Builds the Options object.
-             */
-            public Options build() {
-                return new Options(mLocaleList);
-            }
+        @Nullable
+        public LocaleList getDefaultLocales() {
+            return mDefaultLocales;
         }
-        public @Nullable LocaleList getDefaultLocales() {
-            return mLocaleList;
-        }
-    };
+    }
 
     /**
      * A function to create spans from TextLinks.
@@ -204,13 +193,10 @@
      * @hide
      */
     public static final Function<TextLink, ClickableSpan> DEFAULT_SPAN_FACTORY =
-            new Function<TextLink, ClickableSpan>() {
-        @Override
-        public ClickableSpan apply(TextLink textLink) {
-            // TODO: Implement.
-            throw new UnsupportedOperationException("Not yet implemented");
-        }
-    };
+            textLink -> {
+                // TODO: Implement.
+                throw new UnsupportedOperationException("Not yet implemented");
+            };
 
     /**
      * A builder to construct a TextLinks instance.
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 0a67954..480b27a 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -26,6 +26,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Information about where text selection should be.
@@ -114,8 +115,8 @@
 
     @Override
     public String toString() {
-        return String.format("TextSelection {%d, %d, %s}",
-                mStartIndex, mEndIndex, mEntityConfidence);
+        return String.format(Locale.US,
+                "TextSelection {%d, %d, %s}", mStartIndex, mEndIndex, mEntityConfidence);
     }
 
     /**
@@ -185,7 +186,7 @@
     }
 
     /**
-     * TextSelection optional input parameters.
+     * Optional input parameters for generating TextSelection.
      */
     public static final class Options {
 
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 517ad07..46c3983 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -364,13 +364,13 @@
     }
 
     /**
-     * Notify the host application to handle a SSL client certificate
-     * request. The host application is responsible for showing the UI
-     * if desired and providing the keys. There are three ways to
-     * respond: proceed(), cancel() or ignore(). Webview stores the response
-     * in memory (for the life of the application) if proceed() or cancel() is
-     * called and does not call {@code onReceivedClientCertRequest()} again for the
-     * same host and port pair. Webview does not store the response if ignore()
+     * Notify the host application to handle a SSL client certificate request. The host application
+     * is responsible for showing the UI if desired and providing the keys. There are three ways to
+     * respond: {@link ClientCertRequest#proceed}, {@link ClientCertRequest#cancel}, or {@link
+     * ClientCertRequest#ignore}. Webview stores the response in memory (for the life of the
+     * application) if {@link ClientCertRequest#proceed} or {@link ClientCertRequest#cancel} is
+     * called and does not call {@code onReceivedClientCertRequest()} again for the same host and
+     * port pair. Webview does not store the response if {@link ClientCertRequest#ignore}
      * is called. Note that, multiple layers in chromium network stack might be
      * caching the responses, so the behavior for ignore is only a best case
      * effort.
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4d3189e..b379280 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1952,7 +1952,8 @@
             CharSequence beforeText = mInputText.getText();
             if (!text.equals(beforeText.toString())) {
                 mInputText.setText(text);
-                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                        AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
                     mInputText.onInitializeAccessibilityEvent(event);
@@ -2612,7 +2613,7 @@
         }
 
         private void sendAccessibilityEventForVirtualText(int eventType) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 mInputText.onInitializeAccessibilityEvent(event);
                 mInputText.onPopulateAccessibilityEvent(event);
@@ -2623,7 +2624,7 @@
 
         private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
                 String text) {
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 event.setClassName(Button.class.getName());
                 event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d9bc51f..71532a7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10836,6 +10836,10 @@
 
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
             int fromIndex, int removedCount, int addedCount) {
+        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(
+                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
+            return;
+        }
         AccessibilityEvent event =
                 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
         event.setFromIndex(fromIndex);
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index d807120..bfde6ac 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -504,7 +504,8 @@
         private void trySendAccessibilityEvent() {
             AccessibilityManager accessibilityManager =
                     AccessibilityManager.getInstance(mView.getContext());
-            if (!accessibilityManager.isEnabled()) {
+            if (!accessibilityManager.isObservedEventType(
+                    AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
                 return;
             }
             // treat toasts as notifications since they are used to
diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java
index d38ea2c1..24f0b0c 100644
--- a/core/java/com/android/internal/view/TooltipPopup.java
+++ b/core/java/com/android/internal/view/TooltipPopup.java
@@ -142,7 +142,7 @@
         mTmpAnchorPos[1] -= mTmpAppPos[1];
         // mTmpAnchorPos is now relative to the main app window.
 
-        outParams.x = mTmpAnchorPos[0] + offsetX - mTmpDisplayFrame.width() / 2;
+        outParams.x = mTmpAnchorPos[0] + offsetX - appView.getWidth() / 2;
 
         final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
         mContentView.measure(spec, spec);
@@ -157,6 +157,9 @@
                 outParams.y = yBelow;
             }
         } else {
+            // Use mTmpDisplayFrame.height() as the lower boundary instead of appView.getHeight(),
+            // as the latter includes the navigation bar, and tooltips do not look good over
+            // the navigation bar.
             if (yBelow + tooltipHeight <= mTmpDisplayFrame.height()) {
                 outParams.y = yBelow;
             } else {
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 50ad547..759a41a 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -186,6 +186,9 @@
         }
 
         final AccessibilityEvent event = createEvent(virtualViewId, eventType);
+        if (event == null) {
+            return false;
+        }
         return parent.requestSendAccessibilityEvent(mView, event);
     }
 
@@ -240,6 +243,9 @@
             if (parent != null) {
                 final AccessibilityEvent event = createEvent(virtualViewId,
                         AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                if (event == null) {
+                    return;
+                }
                 event.setContentChangeTypes(changeTypes);
                 parent.requestSendAccessibilityEvent(mView, event);
             }
@@ -305,6 +311,9 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForHost(int eventType) {
+        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            return null;
+        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         mView.onInitializeAccessibilityEvent(event);
 
@@ -325,6 +334,9 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
+        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            return null;
+        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         event.setEnabled(true);
         event.setClassName(DEFAULT_CLASS_NAME);
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index d7300c4..601bd98 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -25,6 +25,7 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
+#include "scoped_nullable_primitive_array.h"
 #include <cstdint>
 #include <vector>
 #include <list>
@@ -87,9 +88,8 @@
 static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
                         jfloatArray recycleWidths, jfloatArray recycleAscents,
                         jfloatArray recycleDescents, jintArray recycleFlags,
-                        jint recycleLength, size_t nBreaks, const jint* breaks,
-                        const jfloat* widths, const jfloat* ascents, const jfloat* descents,
-                        const jint* flags) {
+                        jint recycleLength, const minikin::LineBreakResult& result) {
+    const size_t nBreaks = result.breakPoints.size();
     if ((size_t)recycleLength < nBreaks) {
         // have to reallocate buffers
         recycleBreaks = env->NewIntArray(nBreaks);
@@ -105,11 +105,11 @@
         env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
     }
     // copy data
-    env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
-    env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
-    env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, ascents);
-    env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, descents);
-    env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
+    env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data());
+    env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data());
+    env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data());
+    env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data());
+    env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data());
 }
 
 static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
@@ -136,34 +136,22 @@
     minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
 
     ScopedCharArrayRO text(env, javaText);
+    ScopedNullableIntArrayRO tabStops(env, variableTabStops);
 
-    // TODO: Reorganize minikin APIs.
-    minikin::LineBreaker b(minikin::U16StringPiece(text.get(), length));
-    if (variableTabStops == nullptr) {
-        b.setTabStops(nullptr, 0, defaultTabStop);
-    } else {
-        ScopedIntArrayRO stops(env, variableTabStops);
-        b.setTabStops(stops.get(), stops.size(), defaultTabStop);
-    }
-    b.setStrategy(builder->getStrategy());
-    b.setHyphenationFrequency(builder->getFrequency());
-    b.setJustified(builder->isJustified());
-    b.setLineWidthDelegate(builder->buildLineWidthDelegate(
-            firstWidth, firstWidthLineCount, restWidth, indentsOffset));
-
-    builder->addRuns(&b);
-
-    size_t nBreaks = b.computeBreaks();
+    minikin::U16StringPiece u16Text(text.get(), length);
+    minikin::MeasuredText measuredText = builder->measureText(u16Text);
+    minikin::LineBreakResult result = builder->computeBreaks(
+            u16Text, measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
+            tabStops.get(), tabStops.size(), defaultTabStop);
 
     recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
-            recycleFlags, recycleLength, nBreaks, b.getBreaks(), b.getWidths(), b.getAscents(),
-            b.getDescents(), b.getFlags());
+            recycleFlags, recycleLength, result);
 
-    env->SetFloatArrayRegion(charWidths, 0, b.size(), b.charWidths());
+    env->SetFloatArrayRegion(charWidths, 0, measuredText.widths.size(), measuredText.widths.data());
 
     builder->clearRuns();
 
-    return static_cast<jint>(nBreaks);
+    return static_cast<jint>(result.breakPoints.size());
 }
 
 // Basically similar to Paint.getTextRunAdvances but with C++ interface
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 421e0de..3ad4da6 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -517,7 +517,23 @@
         jobject graphicBuffer) {
     Surface* surface = reinterpret_cast<Surface*>(nativeObject);
     sp<GraphicBuffer> bp = graphicBufferForJavaObject(env, graphicBuffer);
-    int err = Surface::attachAndQueueBuffer(surface, bp);
+    if (bp == nullptr) {
+        return BAD_VALUE;
+    }
+    int err = ((ANativeWindow*)surface)->perform(surface, NATIVE_WINDOW_API_CONNECT,
+            NATIVE_WINDOW_API_CPU);
+    if (err != OK) {
+        return err;
+    }
+    err = surface->attachBuffer(bp->getNativeBuffer());
+    if (err != OK) {
+        return err;
+    }
+    err = ((ANativeWindow*)surface)->queueBuffer(surface, bp->getNativeBuffer(), -1);
+    if (err != OK) {
+        return err;
+    }
+    err = surface->disconnect(NATIVE_WINDOW_API_CPU);
     return err;
 }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8c968a2..f77e6c4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -167,7 +167,7 @@
         maxLayer = INT32_MAX;
     }
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::capture(displayToken,
+    status_t res = ScreenshotClient::captureToBuffer(displayToken,
             sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform,
             rotation, &buffer);
     if (res != NO_ERROR) {
@@ -201,18 +201,15 @@
         maxLayer = INT32_MAX;
     }
 
-    sp<GraphicBuffer> buffer;
-    res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
-        minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer);
+    res = screenshot->update(displayToken, sourceCrop, width, height,
+        minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation));
     if (res != NO_ERROR) {
         return NULL;
     }
 
     SkColorType colorType;
     SkAlphaType alphaType;
-
-    PixelFormat format = buffer->getPixelFormat();
-    switch (format) {
+    switch (screenshot->getFormat()) {
         case PIXEL_FORMAT_RGBX_8888: {
             colorType = kRGBA_8888_SkColorType;
             alphaType = kOpaque_SkAlphaType;
@@ -238,20 +235,66 @@
         }
     }
 
-    SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
-                                         colorType, alphaType,
-                                         SkColorSpace::MakeSRGB());
+    sk_sp<SkColorSpace> colorSpace;
+    if (screenshot->getDataSpace() == HAL_DATASPACE_DISPLAY_P3) {
+        colorSpace = SkColorSpace::MakeRGB(
+                SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
+    } else {
+        colorSpace = SkColorSpace::MakeSRGB();
+    }
 
-    auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
-    return bitmap::createBitmap(env, bitmap.release(),
-                                android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
+    SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(),
+                                                   screenshot->getHeight(),
+                                                   colorType,
+                                                   alphaType,
+                                                   colorSpace);
+
+    const size_t rowBytes =
+            screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
+
+    if (!screenshotInfo.width() || !screenshotInfo.height()) {
+        return NULL;
+    }
+
+    auto bitmap = new Bitmap(
+            (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
+            screenshotInfo, rowBytes);
+    screenshot.release();
+    bitmap->setImmutable();
+    return bitmap::createBitmap(env, bitmap,
+            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) {
+    if (displayToken != NULL) {
+        sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
+        if (consumer != NULL) {
+            int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
+            int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
+            int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
+            int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
+            Rect sourceCrop(left, top, right, bottom);
+
+            if (allLayers) {
+                minLayer = INT32_MIN;
+                maxLayer = INT32_MAX;
+            }
+            ScreenshotClient::capture(displayToken,
+                    consumer->getIGraphicBufferProducer(), sourceCrop,
+                    width, height, minLayer, maxLayer,
+                    useIdentityTransform);
+        }
+    }
+}
+
+static void nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
+        jobject surfaceObj, jobject sourceCropObj, jfloat frameScale) {
+
+    sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken);
+    if (layerHandle == NULL) {
         return;
     }
 
@@ -265,19 +308,11 @@
         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);
+    ScreenshotClient::captureLayers(layerHandle, consumer->getIGraphicBufferProducer(), sourceCrop,
+            frameScale);
 }
 
-static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
+static jobject nativeCaptureLayersToBuffer(JNIEnv* env, jclass clazz, jobject layerHandleToken,
         jobject sourceCropObj, jfloat frameScale) {
 
     sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken);
@@ -291,7 +326,8 @@
     }
 
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::captureLayers(layerHandle, sourceCrop, frameScale, &buffer);
+    status_t res = ScreenshotClient::captureLayersToBuffer(layerHandle, sourceCrop, frameScale,
+            &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -974,8 +1010,10 @@
     {"nativeScreenshotToBuffer",
      "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;",
      (void*)nativeScreenshotToBuffer },
-    {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
+    {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;F)V",
             (void*)nativeCaptureLayers },
+    {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
+            (void*)nativeCaptureLayersToBuffer },
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/scoped_nullable_primitive_array.h b/core/jni/scoped_nullable_primitive_array.h
new file mode 100644
index 0000000..77f4c9d
--- /dev/null
+++ b/core/jni/scoped_nullable_primitive_array.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#ifndef SCOPED_NULLABLE_PRIMITIVE_ARRAY_H
+#define SCOPED_NULLABLE_PRIMITIVE_ARRAY_H
+
+#include <jni.h>
+
+namespace android {
+
+#define ARRAY_TRAITS(ARRAY_TYPE, POINTER_TYPE, NAME)                                  \
+class NAME ## ArrayTraits {                                                           \
+public:                                                                               \
+    static constexpr void getArrayRegion(JNIEnv* env, ARRAY_TYPE array, size_t start, \
+                                         size_t len, POINTER_TYPE out) {              \
+        env->Get ## NAME ## ArrayRegion(array, start, len, out);                      \
+    }                                                                                 \
+                                                                                      \
+    static constexpr POINTER_TYPE getArrayElements(JNIEnv* env, ARRAY_TYPE array) {   \
+        return env->Get ## NAME ## ArrayElements(array, nullptr);                     \
+    }                                                                                 \
+                                                                                      \
+    static constexpr void releaseArrayElements(JNIEnv* env, ARRAY_TYPE array,         \
+                                               POINTER_TYPE buffer, jint mode) {      \
+        env->Release ## NAME ## ArrayElements(array, buffer, mode);                   \
+    }                                                                                 \
+};                                                                                    \
+
+ARRAY_TRAITS(jbooleanArray, jboolean*, Boolean)
+ARRAY_TRAITS(jbyteArray, jbyte*, Byte)
+ARRAY_TRAITS(jcharArray, jchar*, Char)
+ARRAY_TRAITS(jdoubleArray, jdouble*, Double)
+ARRAY_TRAITS(jfloatArray, jfloat*, Float)
+ARRAY_TRAITS(jintArray, jint*, Int)
+ARRAY_TRAITS(jlongArray, jlong*, Long)
+ARRAY_TRAITS(jshortArray, jshort*, Short)
+
+#undef ARRAY_TRAITS
+
+template<typename JavaArrayType, typename PrimitiveType, class Traits, size_t preallocSize = 10>
+class ScopedArrayRO {
+public:
+    ScopedArrayRO(JNIEnv* env, JavaArrayType javaArray) : mEnv(env), mJavaArray(javaArray) {
+        if (mJavaArray == nullptr) {
+            mSize = 0;
+            mRawArray = nullptr;
+        } else {
+            mSize = mEnv->GetArrayLength(mJavaArray);
+            if (mSize <= preallocSize) {
+                Traits::getArrayRegion(mEnv, mJavaArray, 0, mSize, mBuffer);
+                mRawArray = mBuffer;
+            } else {
+                mRawArray = Traits::getArrayElements(mEnv, mJavaArray);
+            }
+        }
+    }
+
+    ~ScopedArrayRO() {
+        if (mRawArray != nullptr && mRawArray != mBuffer) {
+            Traits::releaseArrayElements(mEnv, mJavaArray, mRawArray, JNI_ABORT);
+        }
+    }
+
+    const PrimitiveType* get() const { return mRawArray; }
+    const PrimitiveType& operator[](size_t n) const { return mRawArray[n]; }
+    size_t size() const { return mSize; }
+
+private:
+    JNIEnv* const mEnv;
+    JavaArrayType mJavaArray;
+    PrimitiveType* mRawArray;
+    size_t mSize;
+    PrimitiveType mBuffer[preallocSize];
+    DISALLOW_COPY_AND_ASSIGN(ScopedArrayRO);
+};
+
+// ScopedNullable***ArrayRO provide convenient read-only access to Java array from JNI code.
+// These accept nullptr. In that case, get() returns nullptr and size() returns 0.
+using ScopedNullableBooleanArrayRO = ScopedArrayRO<jbooleanArray, jboolean, BooleanArrayTraits>;
+using ScopedNullableByteArrayRO = ScopedArrayRO<jbyteArray, jbyte, ByteArrayTraits>;
+using ScopedNullableCharArrayRO = ScopedArrayRO<jcharArray, jchar, CharArrayTraits>;
+using ScopedNullableDoubleArrayRO = ScopedArrayRO<jdoubleArray, jdouble, DoubleArrayTraits>;
+using ScopedNullableFloatArrayRO = ScopedArrayRO<jfloatArray, jfloat, FloatArrayTraits>;
+using ScopedNullableIntArrayRO = ScopedArrayRO<jintArray, jint, IntArrayTraits>;
+using ScopedNullableLongArrayRO = ScopedArrayRO<jlongArray, jlong, LongArrayTraits>;
+using ScopedNullableShortArrayRO = ScopedArrayRO<jshortArray, jshort, ShortArrayTraits>;
+
+}  // namespace android
+
+#endif  // SCOPED_NULLABLE_PRIMITIVE_ARRAY_H
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 171f74f..5f756fa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3169,6 +3169,10 @@
          classes are instantiated in the order of the array. -->
     <string-array translatable="false" name="config_deviceSpecificSystemServices"></string-array>
 
+    <!-- Class name of the device specific implementation to replace the DevicePolicyManagerService
+         or empty if the default should be used. -->
+    <string translatable="false" name="config_deviceSpecificDevicePolicyManagerService"></string>
+
     <!-- Component name of media projection permission dialog -->
     <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a115816..ad947b7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -459,6 +459,7 @@
   <java-symbol type="bool" name="config_hasPermanentDpad" />
   <java-symbol type="bool" name="config_useDefaultFocusHighlight" />
   <java-symbol type="array" name="config_deviceSpecificSystemServices" />
+  <java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index d63d00c..39310a8 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1125,6 +1125,7 @@
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
         <!-- Color palette -->
+        <item name="colorBackground">@color/background_device_default_light</item>
         <item name="colorPrimary">@color/primary_device_default_settings_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 5f6f62a..978ea7a 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -45,8 +45,9 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 abstract class AbstractCrossUserContentResolverTest {
-    private final static int TIMEOUT_SERVICE_CONNECTION_SEC = 4;
-    private final static int TIMEOUT_CONTENT_CHANGE_SEC = 4;
+    private static final int TIMEOUT_SERVICE_CONNECTION_SEC = 4;
+    private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4;
+    private static final int TIMEOUT_USER_UNLOCK_SEC = 4;
 
     private Context mContext;
     protected UserManager mUm;
@@ -61,7 +62,7 @@
         mCrossUserId = userInfo.id;
         final PackageManager pm = mContext.getPackageManager();
         pm.installExistingPackageAsUser(mContext.getPackageName(), mCrossUserId);
-        ActivityManager.getService().startUserInBackground(mCrossUserId);
+        unlockUser();
 
         final CountDownLatch connectionLatch = new CountDownLatch(1);
         mServiceConnection = new CrossUserContentServiceConnection(connectionLatch);
@@ -77,6 +78,30 @@
 
     protected abstract UserInfo createUser() throws RemoteException ;
 
+    private void unlockUser() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)
+                        == mCrossUserId) {
+                    latch.countDown();
+                }
+            }
+        };
+        mContext.registerReceiverAsUser(receiver, UserHandle.of(mCrossUserId),
+                new IntentFilter(Intent.ACTION_USER_UNLOCKED), null, null);
+        ActivityManager.getService().startUserInBackground(mCrossUserId);
+
+        try {
+            if (!latch.await(TIMEOUT_USER_UNLOCK_SEC, TimeUnit.SECONDS)) {
+                fail("Timed out waiting for the u" + mCrossUserId + " to unlock");
+            }
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
     @After
     public void tearDown() throws Exception {
         if (mCrossUserId != -1) {
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 454d209..4ce6029 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -231,6 +231,7 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                    Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                     Settings.Global.LOCK_SOUND,
                     Settings.Global.LOW_BATTERY_SOUND,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 41686fa..9092c85 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -45,6 +45,9 @@
 
     private TextClassificationManager mTcm;
     private TextClassifier mClassifier;
+    private TextSelection.Options mSelectionOptions;
+    private TextClassification.Options mClassificationOptions;
+    private TextLinks.Options mLinksOptions;
 
     @Before
     public void setup() {
@@ -52,6 +55,9 @@
                 .getSystemService(TextClassificationManager.class);
         mTcm.setTextClassifier(null);
         mClassifier = mTcm.getTextClassifier();
+        mSelectionOptions = new TextSelection.Options().setDefaultLocales(LOCALES);
+        mClassificationOptions = new TextClassification.Options().setDefaultLocales(LOCALES);
+        mLinksOptions = new TextLinks.Options().setDefaultLocales(LOCALES);
     }
 
     @Test
@@ -66,24 +72,9 @@
         int smartStartIndex = text.indexOf(suggested);
         int smartEndIndex = smartStartIndex + suggested.length();
 
-        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
-                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
-    }
-
-    @Test
-    public void testSmartSelection_nullLocaleList() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Contact me at droid@android.com";
-        String selected = "droid";
-        String suggested = "droid@android.com";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        int smartStartIndex = text.indexOf(suggested);
-        int smartEndIndex = smartStartIndex + suggested.length();
-        LocaleList nullLocales = null;
-
-        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, nullLocales),
+        TextSelection selection = mClassifier.suggestSelection(
+                text, startIndex, endIndex, mSelectionOptions);
+        assertThat(selection,
                 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
     }
 
@@ -99,7 +90,9 @@
         int smartStartIndex = text.indexOf(suggested);
         int smartEndIndex = smartStartIndex + suggested.length();
 
-        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+        TextSelection selection = mClassifier.suggestSelection(
+                text, startIndex, endIndex, mSelectionOptions);
+        assertThat(selection,
                 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
     }
 
@@ -112,7 +105,9 @@
         int startIndex = text.indexOf(selected);
         int endIndex = startIndex + selected.length();
 
-        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+        TextSelection selection = mClassifier.suggestSelection(
+                text, startIndex, endIndex, mSelectionOptions);
+        assertThat(selection,
                 isTextSelection(startIndex, endIndex, NO_TYPE));
     }
 
@@ -124,7 +119,10 @@
         String classifiedText = "droid@android.com";
         int startIndex = text.indexOf(classifiedText);
         int endIndex = startIndex + classifiedText.length();
-        assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES),
+
+        TextClassification classification = mClassifier.classifyText(
+                text, startIndex, endIndex, mClassificationOptions);
+        assertThat(classification,
                 isTextClassification(
                         classifiedText,
                         TextClassifier.TYPE_EMAIL,
@@ -139,7 +137,10 @@
         String classifiedText = "www.android.com";
         int startIndex = text.indexOf(classifiedText);
         int endIndex = startIndex + classifiedText.length();
-        assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES),
+
+        TextClassification classification = mClassifier.classifyText(
+                text, startIndex, endIndex, mClassificationOptions);
+        assertThat(classification,
                 isTextClassification(
                         classifiedText,
                         TextClassifier.TYPE_URL,
@@ -154,7 +155,10 @@
         String classifiedText = "HTTP://ANDROID.COM";
         int startIndex = text.indexOf(classifiedText);
         int endIndex = startIndex + classifiedText.length();
-        assertThat(mClassifier.classifyText(text, startIndex, endIndex, LOCALES),
+
+        TextClassification classification = mClassifier.classifyText(
+                text, startIndex, endIndex, mClassificationOptions);
+        assertThat(classification,
                 isTextClassification(
                         classifiedText,
                         TextClassifier.TYPE_URL,
@@ -162,22 +166,6 @@
     }
 
     @Test
-    public void testTextClassifyText_nullLocaleList() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Contact me at droid@android.com";
-        String classifiedText = "droid@android.com";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        LocaleList nullLocales = null;
-        assertThat(mClassifier.classifyText(text, startIndex, endIndex, nullLocales),
-                isTextClassification(
-                        classifiedText,
-                        TextClassifier.TYPE_EMAIL,
-                        "mailto:" + classifiedText));
-    }
-
-    @Test
     public void testGenerateLinks() {
         if (isTextClassifierDisabled()) return;
 
@@ -210,13 +198,14 @@
         int startIndex = text.indexOf(classifiedText);
         int endIndex = startIndex + classifiedText.length();
 
-        Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, null).getLinks();
+        Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, mLinksOptions)
+                .getLinks();
         for (TextLinks.TextLink link : links) {
             if (text.subSequence(link.getStart(), link.getEnd()).equals(classifiedText)) {
                 assertEquals(type, link.getEntity(0));
                 assertEquals(startIndex, link.getStart());
                 assertEquals(endIndex, link.getEnd());
-                assertTrue(link.getConfidenceScore(type) > .9);
+                assertTrue(link.getConfidenceScore(type) > 0);
                 return;
             }
         }
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index 682a002..4b197e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -104,7 +104,9 @@
 
     @Test
     public void testCpuFreqTimes() throws Exception {
-        assumeTrue(sCpuFreqTimesAvailable);
+        if (!sCpuFreqTimesAvailable) {
+            return;
+        }
 
         batteryOnScreenOn();
         forceStop();
@@ -126,7 +128,9 @@
 
     @Test
     public void testCpuFreqTimes_screenOff() throws Exception {
-        assumeTrue(sCpuFreqTimesAvailable);
+        if (!sCpuFreqTimesAvailable) {
+            return;
+        }
 
         batteryOnScreenOff();
         forceStop();
@@ -154,7 +158,9 @@
 
     @Test
     public void testCpuFreqTimes_isolatedProcess() throws Exception {
-        assumeTrue(sCpuFreqTimesAvailable);
+        if (!sCpuFreqTimesAvailable) {
+            return;
+        }
 
         batteryOnScreenOn();
         forceStop();
diff --git a/core/tests/overlaytests/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/OverlayAppFiltered/Android.mk
index 8ba21df..f76de7a 100644
--- a/core/tests/overlaytests/OverlayAppFiltered/Android.mk
+++ b/core/tests/overlaytests/OverlayAppFiltered/Android.mk
@@ -3,8 +3,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES += legacy-test
-
 LOCAL_SDK_VERSION := system_current
 
 LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay
diff --git a/core/tests/overlaytests/OverlayAppFirst/Android.mk b/core/tests/overlaytests/OverlayAppFirst/Android.mk
index 51f4487..bf9416c 100644
--- a/core/tests/overlaytests/OverlayAppFirst/Android.mk
+++ b/core/tests/overlaytests/OverlayAppFirst/Android.mk
@@ -3,8 +3,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES += legacy-test
-
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := com.android.overlaytest.first_app_overlay
diff --git a/core/tests/overlaytests/OverlayAppSecond/Android.mk b/core/tests/overlaytests/OverlayAppSecond/Android.mk
index b3cfd18..bb7d142 100644
--- a/core/tests/overlaytests/OverlayAppSecond/Android.mk
+++ b/core/tests/overlaytests/OverlayAppSecond/Android.mk
@@ -3,8 +3,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES += legacy-test
-
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := com.android.overlaytest.second_app_overlay
diff --git a/core/tests/overlaytests/OverlayTest/Android.mk b/core/tests/overlaytests/OverlayTest/Android.mk
index 964348f..5fe7b91 100644
--- a/core/tests/overlaytests/OverlayTest/Android.mk
+++ b/core/tests/overlaytests/OverlayTest/Android.mk
@@ -7,7 +7,7 @@
 
 LOCAL_DEX_PREOPT := false
 
-LOCAL_JAVA_LIBRARIES += legacy-test
+LOCAL_JAVA_LIBRARIES += android.test.base
 
 LOCAL_MODULE_PATH := $(TARGET_OUT)/app
 
diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
index 5265d91..ed330467 100644
--- a/core/tests/overlaytests/OverlayTestOverlay/Android.mk
+++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
@@ -3,8 +3,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES += legacy-test
-
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 83c82af..94a05b2 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -427,7 +427,6 @@
   ATRACE_CALL();
   constexpr const int kMaxIterations = 20;
 
-  *out_last_reference = 0u;
   for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
                               in_out_value->data != 0u && iteration < kMaxIterations;
        iteration++) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index a77c4b9..b033137 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -210,7 +210,7 @@
   // are OR'd together with `in_out_flags`.
   // `in_out_config` is populated with the configuration for which the resolved value was defined.
   // `out_last_reference` is populated with the last reference ID before resolving to an actual
-  // value.
+  // value. This is only initialized if the passed in `in_out_value` is a reference.
   // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
   // it was not found.
   ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index ab1a22e..567adfe 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -319,7 +319,7 @@
   EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
   EXPECT_EQ(basic::R::integer::ref2, value.data);
 
-  uint32_t last_ref;
+  uint32_t last_ref = 0u;
   cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
   ASSERT_NE(kInvalidCookie, cookie);
   EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
@@ -342,7 +342,7 @@
   EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
   EXPECT_EQ(basic::R::array::integerArray1, value.data);
 
-  uint32_t last_ref;
+  uint32_t last_ref = 0u;
   cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
   ASSERT_NE(kInvalidCookie, cookie);
   EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
@@ -350,6 +350,25 @@
   EXPECT_EQ(basic::R::array::integerArray1, last_ref);
 }
 
+TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  ResTable_config selected_config;
+  memset(&selected_config, 0, sizeof(selected_config));
+
+  uint32_t flags = 0u;
+
+  // Create some kind of Res_value that is NOT a reference.
+  Res_value value;
+  value.dataType = Res_value::TYPE_STRING;
+  value.data = 0;
+
+  uint32_t last_ref = basic::R::string::test1;
+  EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref));
+  EXPECT_EQ(basic::R::string::test1, last_ref);
+}
+
 static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
                                    const ResTable_config& configuration) {
   return configurations.count(configuration) > 0;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 5c577ae..96e4fcf 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -417,3 +417,15 @@
         "tests/microbench/TaskManagerBench.cpp",
     ],
 }
+
+// ----------------------------------------
+// Phony target to build benchmarks for PGO
+// ----------------------------------------
+
+phony {
+    name: "pgo-targets-hwui",
+    required: [
+        "hwuimicro",
+        "hwuimacro",
+    ]
+}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 508869a..eb0d161 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -738,6 +738,7 @@
     // care of all alignment.
     SkPaint paintCopy(paint);
     paintCopy.setTextAlign(SkPaint::kLeft_Align);
+    SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
 
     SkRect bounds =
             SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index 58c9980..6bae80c 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -36,6 +36,7 @@
         SkPaint textPaint;
         textPaint.setTextSize(dp(20));
         textPaint.setAntiAlias(true);
+        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30));
 
         SkPoint pts[2];
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index 75b231d..fee0659 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -42,8 +42,10 @@
 
         mBluePaint.setColor(SkColorSetARGB(255, 0, 0, 255));
         mBluePaint.setTextSize(padding);
+        mBluePaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0));
         mGreenPaint.setTextSize(padding);
+        mGreenPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
         // interleave drawText and drawRect with saveLayer ops
         for (int i = 0; i < regions; i++, top += smallRectHeight) {
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index dff259f..7dd271f 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -350,6 +350,72 @@
     EXPECT_EQ(3, canvas.getIndex());
 }
 
+RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
+    class ProjectionTestCanvas : public SkCanvas {
+    public:
+        ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
+        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+            mDrawCounter++;
+        }
+
+        int getDrawCounter() { return mDrawCounter; }
+
+    private:
+        int mDrawCounter = 0;
+    };
+
+    auto receiverBackground = TestUtils::createSkiaNode(
+            0, 0, 100, 100,
+            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+                properties.setProjectionReceiver(true);
+            },
+            "B"); // a receiver with an empty display list
+
+    auto projectingRipple = TestUtils::createSkiaNode(
+            0, 0, 100, 100,
+            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+                properties.setProjectBackwards(true);
+                properties.setClipToBounds(false);
+                SkPaint paint;
+                canvas.drawRect(0, 0, 100, 100, paint);
+            },
+            "P");
+    auto child = TestUtils::createSkiaNode(
+            0, 0, 100, 100,
+            [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+                SkPaint paint;
+                canvas.drawRect(0, 0, 100, 100, paint);
+                canvas.drawRenderNode(projectingRipple.get());
+            },
+            "C");
+    auto parent = TestUtils::createSkiaNode(
+            0, 0, 100, 100,
+            [&receiverBackground, &child](RenderProperties& properties,
+                                          SkiaRecordingCanvas& canvas) {
+                canvas.drawRenderNode(receiverBackground.get());
+                canvas.drawRenderNode(child.get());
+            },
+            "A");
+    ContextFactory contextFactory;
+    std::unique_ptr<CanvasContext> canvasContext(
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+    DamageAccumulator damageAccumulator;
+    info.damageAccumulator = &damageAccumulator;
+    parent->prepareTree(info);
+
+    // parent(A)             -> (receiverBackground, child)
+    // child(C)              -> (rect[0, 0, 100, 100], projectingRipple)
+    // projectingRipple(P)   -> (rect[0, 0, 100, 100]) -> projects backwards
+    // receiverBackground(B) -> (empty) -> projection receiver
+
+    // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
+    ProjectionTestCanvas canvas(100, 100);
+    RenderNodeDrawable drawable(parent.get(), &canvas, true);
+    canvas.drawDrawable(&drawable);
+    EXPECT_EQ(2, canvas.getDrawCounter());
+}
+
 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
     /* R is backward projected on B and C is a layer.
                 A
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 7afe267..e0289f0 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -741,7 +741,7 @@
         /**
          * @hide
          * Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD,
-         * REMOTE_SUBMIX and RADIO_TUNER.
+         * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK and VOICE_CALL.
          * @param preset
          * @return the same Builder instance.
          */
@@ -749,7 +749,10 @@
         public Builder setInternalCapturePreset(int preset) {
             if ((preset == MediaRecorder.AudioSource.HOTWORD)
                     || (preset == MediaRecorder.AudioSource.REMOTE_SUBMIX)
-                    || (preset == MediaRecorder.AudioSource.RADIO_TUNER)) {
+                    || (preset == MediaRecorder.AudioSource.RADIO_TUNER)
+                    || (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK)
+                    || (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
+                    || (preset == MediaRecorder.AudioSource.VOICE_CALL)) {
                 mSource = preset;
             } else {
                 setCapturePreset(preset);
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 0906ba5..27784e9 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1516,66 +1516,13 @@
     }
 
     /**
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeRoutingEventHandlerDelegate {
-        private final Handler mHandler;
-
-        NativeRoutingEventHandlerDelegate(final AudioRecord record,
-                                   final AudioRouting.OnRoutingChangedListener listener,
-                                   Handler handler) {
-            // find the looper for our new event handler
-            Looper looper;
-            if (handler != null) {
-                looper = handler.getLooper();
-            } else {
-                // no given handler, use the looper the AudioRecord was created in
-                looper = mInitializationLooper;
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (record == null) {
-                            return;
-                        }
-                        switch(msg.what) {
-                        case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
-                            if (listener != null) {
-                                listener.onRoutingChanged(record);
-                            }
-                            break;
-                        default:
-                            loge("Unknown native event type: " + msg.what);
-                            break;
-                        }
-                    }
-                };
-            } else {
-                mHandler = null;
-            }
-        }
-
-        Handler getHandler() {
-            return mHandler;
-        }
-    }
-
-    /**
      * Sends device list change notification to all listeners.
      */
     private void broadcastRoutingChange() {
         AudioManager.resetAudioPortGeneration();
         synchronized (mRoutingChangeListeners) {
             for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
-                Handler handler = delegate.getHandler();
-                if (handler != null) {
-                    handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
-                }
+                delegate.notifyClient();
             }
         }
     }
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 50145f8..e535fdf 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2856,10 +2856,7 @@
         AudioManager.resetAudioPortGeneration();
         synchronized (mRoutingChangeListeners) {
             for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
-                Handler handler = delegate.getHandler();
-                if (handler != null) {
-                    handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
-                }
+                delegate.notifyClient();
             }
         }
     }
@@ -2943,56 +2940,6 @@
         }
     }
 
-    /**
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeRoutingEventHandlerDelegate {
-        private final Handler mHandler;
-
-        NativeRoutingEventHandlerDelegate(final AudioTrack track,
-                                   final AudioRouting.OnRoutingChangedListener listener,
-                                   Handler handler) {
-            // find the looper for our new event handler
-            Looper looper;
-            if (handler != null) {
-                looper = handler.getLooper();
-            } else {
-                // no given handler, use the looper the AudioTrack was created in
-                looper = mInitializationLooper;
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (track == null) {
-                            return;
-                        }
-                        switch(msg.what) {
-                        case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
-                            if (listener != null) {
-                                listener.onRoutingChanged(track);
-                            }
-                            break;
-                        default:
-                            loge("Unknown native event type: " + msg.what);
-                            break;
-                        }
-                    }
-                };
-            } else {
-                mHandler = null;
-            }
-        }
-
-        Handler getHandler() {
-            return mHandler;
-        }
-    }
-
     //---------------------------------------------------------
     // Methods for IPlayer interface
     //--------------------
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index c78c99f..1019580 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -640,7 +640,6 @@
      * The ImageReader continues to be usable after this call, but may need to reallocate buffers
      * when more buffers are needed for rendering.
      * </p>
-     * @hide
      */
     public void discardFreeBuffers() {
         synchronized (mCloseLock) {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 649c091..1bc3dfa 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1514,7 +1514,8 @@
             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
                 enableNativeRoutingCallbacksLocked(true);
                 mRoutingChangeListeners.put(
-                        listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+                        listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                                handler != null ? handler : mEventHandler));
             }
         }
     }
@@ -1535,36 +1536,6 @@
         }
     }
 
-    /**
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeRoutingEventHandlerDelegate {
-        private MediaPlayer mMediaPlayer;
-        private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
-        private Handler mHandler;
-
-        NativeRoutingEventHandlerDelegate(final MediaPlayer mediaPlayer,
-                final AudioRouting.OnRoutingChangedListener listener, Handler handler) {
-            mMediaPlayer = mediaPlayer;
-            mOnRoutingChangedListener = listener;
-            mHandler = handler != null ? handler : mEventHandler;
-        }
-
-        void notifyClient() {
-            if (mHandler != null) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mOnRoutingChangedListener != null) {
-                            mOnRoutingChangedListener.onRoutingChanged(mMediaPlayer);
-                        }
-                    }
-                });
-            }
-        }
-    }
-
     private native final boolean native_setOutputDevice(int deviceId);
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback(boolean enabled);
diff --git a/media/java/android/media/NativeRoutingEventHandlerDelegate.java b/media/java/android/media/NativeRoutingEventHandlerDelegate.java
new file mode 100644
index 0000000..9a6baf1
--- /dev/null
+++ b/media/java/android/media/NativeRoutingEventHandlerDelegate.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Handler;
+
+/**
+ * Helper class {@link AudioTrack}, {@link AudioRecord}, {@link MediaPlayer} and {@link MediaRecorder}
+ * to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread.
+ * @hide
+ */
+class NativeRoutingEventHandlerDelegate {
+    private AudioRouting mAudioRouting;
+    private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
+    private Handler mHandler;
+
+    NativeRoutingEventHandlerDelegate(final AudioRouting audioRouting,
+            final AudioRouting.OnRoutingChangedListener listener, Handler handler) {
+        mAudioRouting = audioRouting;
+        mOnRoutingChangedListener = listener;
+        mHandler = handler;
+    }
+
+    void notifyClient() {
+        if (mHandler != null) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mOnRoutingChangedListener != null) {
+                        mOnRoutingChangedListener.onRoutingChanged(mAudioRouting);
+                    }
+                }
+            });
+        }
+    }
+}
diff --git a/media/mca/filterfw/Android.mk b/media/mca/filterfw/Android.mk
index 334f4e2..37f1e13 100644
--- a/media/mca/filterfw/Android.mk
+++ b/media/mca/filterfw/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_MODULE := libfilterfw
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libfilterfw_jni \
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index 56a675a..15ea367 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -195,7 +195,12 @@
             if(!handleObexPacket(packet)) {
                 return;
             }
-            if (!mHasBody) {
+            /* Don't Pre-Send continue when Remote requested for SRM
+             * Let the Application confirm.
+             */
+            if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
+                    + " not hasBody case: " + mHasBody);
+            if (!mHasBody && !mSrmEnabled) {
                 while ((!mGetOperation) && (!finalBitSet)) {
                     sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                     if (mPrivateInput.available() > 0) {
@@ -204,8 +209,13 @@
                 }
             }
         }
-
-        while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
+        /* Don't Pre-Send continue when Remote requested for SRM
+          * Let the Application confirm.
+          */
+        if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
+            + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
+        while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
+                && (mPrivateInput.available() == 0)) {
             sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
             if (mPrivateInput.available() > 0) {
                 break;
diff --git a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
index af2c83f..a017f41 100644
--- a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
+++ b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
@@ -16,7 +16,7 @@
 
 <inset xmlns:android="http://schemas.android.com/apk/res/android">
     <shape android:shape="rectangle">
-        <corners android:radius="2dp" />
+        <corners android:radius="?android:attr/dialogCornerRadius" />
         <solid android:color="?android:attr/colorBackground" />
     </shape>
 </inset>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index ee7885d..0703330 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -18,7 +18,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -27,7 +26,6 @@
 import com.android.settingslib.applications.InterestingConfigChanges;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -104,10 +102,10 @@
         }
         for (int i = 0; i < mCategories.size(); i++) {
             DashboardCategory category = mCategories.get(i);
-            for (int j = 0; j < category.tiles.size(); j++) {
-                Tile tile = category.tiles.get(j);
+            for (int j = 0; j < category.getTilesCount(); j++) {
+                Tile tile = category.getTile(j);
                 if (tileBlacklist.contains(tile.intent.getComponent())) {
-                    category.tiles.remove(j--);
+                    category.removeTile(j--);
                 }
             }
         }
@@ -181,7 +179,7 @@
                         newCategory = new DashboardCategory();
                         categoryByKeyMap.put(newCategoryKey, newCategory);
                     }
-                    newCategory.tiles.add(tile);
+                    newCategory.addTile(tile);
                 }
             }
         }
@@ -198,7 +196,7 @@
     synchronized void sortCategories(Context context,
             Map<String, DashboardCategory> categoryByKeyMap) {
         for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
-            sortCategoriesForExternalTiles(context, categoryEntry.getValue());
+            categoryEntry.getValue().sortTiles(context.getPackageName());
         }
     }
 
@@ -210,16 +208,16 @@
     synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
         for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
             final DashboardCategory category = categoryEntry.getValue();
-            final int count = category.tiles.size();
+            final int count = category.getTilesCount();
             final Set<ComponentName> components = new ArraySet<>();
             for (int i = count - 1; i >= 0; i--) {
-                final Tile tile = category.tiles.get(i);
+                final Tile tile = category.getTile(i);
                 if (tile.intent == null) {
                     continue;
                 }
                 final ComponentName tileComponent = tile.intent.getComponent();
                 if (components.contains(tileComponent)) {
-                    category.tiles.remove(i);
+                    category.removeTile(i);
                 } else {
                     components.add(tileComponent);
                 }
@@ -234,28 +232,7 @@
      */
     private synchronized void sortCategoriesForExternalTiles(Context context,
             DashboardCategory dashboardCategory) {
-        final String skipPackageName = context.getPackageName();
+        dashboardCategory.sortTiles(context.getPackageName());
 
-        // Sort tiles based on [priority, package within priority]
-        Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
-            final String package1 = tile1.intent.getComponent().getPackageName();
-            final String package2 = tile2.intent.getComponent().getPackageName();
-            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
-            // First sort by priority
-            final int priorityCompare = tile2.priority - tile1.priority;
-            if (priorityCompare != 0) {
-                return priorityCompare;
-            }
-            // Then sort by package name, skip package take precedence
-            if (packageCompare != 0) {
-                if (TextUtils.equals(package1, skipPackageName)) {
-                    return -1;
-                }
-                if (TextUtils.equals(package2, skipPackageName)) {
-                    return 1;
-                }
-            }
-            return packageCompare;
-        });
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index f6f8168..a966e82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.drawer;
 
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
+
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -23,6 +25,8 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 public class DashboardCategory implements Parcelable {
@@ -48,39 +52,59 @@
     /**
      * List of the category's children
      */
-    public List<Tile> tiles = new ArrayList<>();
+    private List<Tile> mTiles = new ArrayList<>();
 
+    DashboardCategory(DashboardCategory in) {
+        if (in != null) {
+            title = in.title;
+            key = in.key;
+            priority = in.priority;
+            for (Tile tile : in.mTiles) {
+                mTiles.add(tile);
+            }
+        }
+    }
 
     public DashboardCategory() {
         // Empty
     }
 
+    /**
+     * Get a copy of the list of the category's children.
+     *
+     * Note: the returned list serves as a read-only list. If tiles needs to be added or removed
+     * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}.
+     */
+    public List<Tile> getTiles() {
+        return Collections.unmodifiableList(mTiles);
+    }
+
     public void addTile(Tile tile) {
-        tiles.add(tile);
+        mTiles.add(tile);
     }
 
     public void addTile(int n, Tile tile) {
-        tiles.add(n, tile);
+        mTiles.add(n, tile);
     }
 
     public void removeTile(Tile tile) {
-        tiles.remove(tile);
+        mTiles.remove(tile);
     }
 
     public void removeTile(int n) {
-        tiles.remove(n);
+        mTiles.remove(n);
     }
 
     public int getTilesCount() {
-        return tiles.size();
+        return mTiles.size();
     }
 
     public Tile getTile(int n) {
-        return tiles.get(n);
+        return mTiles.get(n);
     }
 
     public boolean containsComponent(ComponentName component) {
-        for (Tile tile : tiles) {
+        for (Tile tile : mTiles) {
             if (TextUtils.equals(tile.intent.getComponent().getClassName(),
                     component.getClassName())) {
                 if (DEBUG) {
@@ -95,6 +119,40 @@
         return false;
     }
 
+    /**
+     * Sort priority value for tiles in this category.
+     */
+    public void sortTiles() {
+        Collections.sort(mTiles, TILE_COMPARATOR);
+    }
+
+    /**
+     * Sort priority value and package name for tiles in this category.
+     */
+    public void sortTiles(String skipPackageName) {
+        // Sort mTiles based on [priority, package within priority]
+        Collections.sort(mTiles, (tile1, tile2) -> {
+            final String package1 = tile1.intent.getComponent().getPackageName();
+            final String package2 = tile2.intent.getComponent().getPackageName();
+            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
+            // First sort by priority
+            final int priorityCompare = tile2.priority - tile1.priority;
+            if (priorityCompare != 0) {
+                return priorityCompare;
+            }
+            // Then sort by package name, skip package take precedence
+            if (packageCompare != 0) {
+                if (TextUtils.equals(package1, skipPackageName)) {
+                    return -1;
+                }
+                if (TextUtils.equals(package2, skipPackageName)) {
+                    return 1;
+                }
+            }
+            return packageCompare;
+        });
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -106,11 +164,11 @@
         dest.writeString(key);
         dest.writeInt(priority);
 
-        final int count = tiles.size();
+        final int count = mTiles.size();
         dest.writeInt(count);
 
         for (int n = 0; n < count; n++) {
-            Tile tile = tiles.get(n);
+            Tile tile = mTiles.get(n);
             tile.writeToParcel(dest, flags);
         }
     }
@@ -124,7 +182,7 @@
 
         for (int n = 0; n < count; n++) {
             Tile tile = Tile.CREATOR.createFromParcel(in);
-            tiles.add(tile);
+            mTiles.add(tile);
         }
     }
 
@@ -141,4 +199,13 @@
             return new DashboardCategory[size];
         }
     };
+
+    public static final Comparator<Tile> TILE_COMPARATOR =
+            new Comparator<Tile>() {
+                @Override
+                public int compare(Tile lhs, Tile rhs) {
+                    return rhs.priority - lhs.priority;
+                }
+            };
+
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 038dcf8..e986e0f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -253,7 +253,7 @@
         }
         ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
         for (DashboardCategory category : categories) {
-            Collections.sort(category.tiles, TILE_COMPARATOR);
+            category.sortTiles();
         }
         Collections.sort(categories, CATEGORY_COMPARATOR);
         if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
@@ -595,14 +595,6 @@
         return pathSegments.get(0);
     }
 
-    public static final Comparator<Tile> TILE_COMPARATOR =
-            new Comparator<Tile>() {
-        @Override
-        public int compare(Tile lhs, Tile rhs) {
-            return rhs.priority - lhs.priority;
-        }
-    };
-
     private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
             new Comparator<DashboardCategory>() {
         @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
index d7eae5f..f099c90 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
@@ -95,8 +95,9 @@
         mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
 
         assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
-        assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).tiles.size()).isEqualTo(1);
-        assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
+        assertThat(
+                mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1);
+        assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
     }
 
     @Test
@@ -114,9 +115,10 @@
         // Added 1 more category to category map.
         assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
         // The new category map has CATEGORY_NETWORK type now, which contains 1 tile.
-        assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).tiles.size()).isEqualTo(1);
+        assertThat(
+                mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1);
         // Old category still exists.
-        assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
+        assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
     }
 
     @Test
@@ -136,9 +138,9 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile3.priority = 200;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -146,9 +148,9 @@
                 mCategoryByKeyMap);
 
         // Verify they are now sorted.
-        assertThat(category.tiles.get(0)).isSameAs(tile3);
-        assertThat(category.tiles.get(1)).isSameAs(tile1);
-        assertThat(category.tiles.get(2)).isSameAs(tile2);
+        assertThat(category.getTile(0)).isSameAs(tile3);
+        assertThat(category.getTile(1)).isSameAs(tile1);
+        assertThat(category.getTile(2)).isSameAs(tile2);
     }
 
     @Test
@@ -169,9 +171,9 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage1, "class3"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -179,9 +181,9 @@
                 mCategoryByKeyMap);
 
         // Verify they are now sorted.
-        assertThat(category.tiles.get(0)).isSameAs(tile2);
-        assertThat(category.tiles.get(1)).isSameAs(tile1);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        assertThat(category.getTile(0)).isSameAs(tile2);
+        assertThat(category.getTile(1)).isSameAs(tile1);
+        assertThat(category.getTile(2)).isSameAs(tile3);
     }
 
     @Test
@@ -202,9 +204,9 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -212,9 +214,9 @@
                 mCategoryByKeyMap);
 
         // Verify the sorting order is not changed
-        assertThat(category.tiles.get(0)).isSameAs(tile1);
-        assertThat(category.tiles.get(1)).isSameAs(tile2);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        assertThat(category.getTile(0)).isSameAs(tile1);
+        assertThat(category.getTile(1)).isSameAs(tile2);
+        assertThat(category.getTile(2)).isSameAs(tile3);
     }
 
     @Test
@@ -236,10 +238,10 @@
         final Tile tile4 = new Tile();
         tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile4.priority = -1;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
-        category.tiles.add(tile4);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
+        category.addTile(tile4);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -247,10 +249,10 @@
             mCategoryByKeyMap);
 
         // Verify the sorting order is not changed
-        assertThat(category.tiles.get(0)).isSameAs(tile1);
-        assertThat(category.tiles.get(1)).isSameAs(tile2);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
-        assertThat(category.tiles.get(3)).isSameAs(tile4);
+        assertThat(category.getTile(0)).isSameAs(tile1);
+        assertThat(category.getTile(1)).isSameAs(tile2);
+        assertThat(category.getTile(2)).isSameAs(tile3);
+        assertThat(category.getTile(3)).isSameAs(tile4);
     }
 
     @Test
@@ -270,9 +272,9 @@
         final Tile tile3 = new Tile();
         tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
         tile3.priority = 1;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         // Sort their priorities
@@ -280,9 +282,9 @@
             mCategoryByKeyMap);
 
         // Verify the sorting order is internal first, follow by package name ordering
-        assertThat(category.tiles.get(0)).isSameAs(tile2);
-        assertThat(category.tiles.get(1)).isSameAs(tile3);
-        assertThat(category.tiles.get(2)).isSameAs(tile1);
+        assertThat(category.getTile(0)).isSameAs(tile2);
+        assertThat(category.getTile(1)).isSameAs(tile3);
+        assertThat(category.getTile(2)).isSameAs(tile1);
     }
 
     @Test
@@ -303,14 +305,14 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class3"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
 
-        assertThat(category.tiles.size()).isEqualTo(3);
+        assertThat(category.getTilesCount()).isEqualTo(3);
     }
 
     @Test
@@ -331,13 +333,13 @@
         tile3.intent =
                 new Intent().setComponent(new ComponentName(testPackage, "class1"));
         tile3.priority = 50;
-        category.tiles.add(tile1);
-        category.tiles.add(tile2);
-        category.tiles.add(tile3);
+        category.addTile(tile1);
+        category.addTile(tile2);
+        category.addTile(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
         mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
 
-        assertThat(category.tiles.size()).isEqualTo(1);
+        assertThat(category.getTilesCount()).isEqualTo(1);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index dad3a28..a395a4a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -216,7 +216,7 @@
         List<DashboardCategory> categoryList = TileUtils.getCategories(
                 mContext, cache, false /* categoryDefinedInManifest */, testAction,
                 TileUtils.SETTING_PKG);
-        assertThat(categoryList.get(0).tiles.get(0).category).isEqualTo(testCategory);
+        assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
     }
 
     @Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 258c96c..7fb6ede 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -61,6 +61,7 @@
 import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -343,7 +344,8 @@
             }
 
             case Settings.CALL_METHOD_GET_SECURE: {
-                Setting setting = getSecureSetting(name, requestingUserId);
+                Setting setting = getSecureSetting(name, requestingUserId,
+                        /*enableOverride=*/ true);
                 return packageValueForCallResult(setting, isTrackingGeneration(args));
             }
 
@@ -1073,6 +1075,10 @@
     }
 
     private Setting getSecureSetting(String name, int requestingUserId) {
+        return getSecureSetting(name, requestingUserId, /*enableOverride=*/ false);
+    }
+
+    private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")");
         }
@@ -1102,6 +1108,14 @@
                 return getSsaidSettingLocked(callingPkg, owningUserId);
             }
         }
+        if (enableOverride) {
+            if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
+                final Setting overridden = getLocationProvidersAllowedSetting(owningUserId);
+                if (overridden != null) {
+                    return overridden;
+                }
+            }
+        }
 
         // Not the SSAID; do a straight lookup
         synchronized (mLock) {
@@ -1190,6 +1204,35 @@
         return null;
     }
 
+    private Setting getLocationProvidersAllowedSetting(int owningUserId) {
+        synchronized (mLock) {
+            final Setting setting = getGlobalSetting(
+                    Global.LOCATION_GLOBAL_KILL_SWITCH);
+            if (!"1".equals(setting.getValue())) {
+                return null;
+            }
+            // Global kill-switch is enabled. Return an empty value.
+            final SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
+                    SETTINGS_TYPE_SECURE, owningUserId);
+            return settingsState.new Setting(
+                    Secure.LOCATION_PROVIDERS_ALLOWED,
+                    "", // value
+                    "", // tag
+                    "", // default value
+                    "", // package name
+                    false, // from system
+                    "0" // id
+            ) {
+                @Override
+                public boolean update(String value, boolean setDefault, String packageName,
+                        String tag, boolean forceNonSystemPackage) {
+                    Slog.wtf(LOG_TAG, "update shoudln't be called on this instance.");
+                    return false;
+                }
+            };
+        }
+    }
+
     private boolean insertSecureSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, boolean forceNotify) {
         if (DEBUG) {
@@ -2780,6 +2823,12 @@
             }
 
             mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
+
+            // When the global kill switch is updated, send the change notification for
+            // the location setting.
+            if (isGlobalSettingsKey(key) && Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)) {
+                notifyLocationChangeForRunningUsers();
+            }
         }
 
         private void maybeNotifyProfiles(int type, int userId, Uri uri, String name,
@@ -2799,6 +2848,24 @@
             }
         }
 
+        private void notifyLocationChangeForRunningUsers() {
+            final List<UserInfo> users = mUserManager.getUsers(/*excludeDying=*/ true);
+
+            for (int i = 0; i < users.size(); i++) {
+                final int userId = users.get(i).id;
+
+                if (!mUserManager.isUserRunning(UserHandle.of(userId))) {
+                    continue;
+                }
+
+                final int key = makeKey(SETTINGS_TYPE_GLOBAL, userId);
+                final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED);
+
+                mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
+                        userId, 0, uri).sendToTarget();
+            }
+        }
+
         private boolean isGlobalSettingsKey(int key) {
             return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
         }
@@ -2885,7 +2952,7 @@
                         } catch (SecurityException e) {
                             Slog.w(LOG_TAG, "Failed to notify for " + userId + ": " + uri, e);
                         }
-                        if (DEBUG) {
+                        if (DEBUG || true) {
                             Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri);
                         }
                     } break;
diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml
new file mode 100644
index 0000000..3c5f01b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_account_circle.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48.0dp"
+        android:height="48.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M24,0C10.8,0 0,10.8 0,24s10.8,24 24,24s24,-10.8 24,-24S37.200001,0 24,0zM24,7.2c3.96,0 7.2,3.24 7.2,7.2s-3.24,7.2 -7.2,7.2s-7.2,-3.24 -7.2,-7.2S20.040001,7.2 24,7.2zM24,41.279999c-6,0 -11.28,-3.12 -14.4,-7.68c0.12,-4.8 9.6,-7.44 14.4,-7.44s14.28,2.64 14.4,7.44C35.279999,38.16 30,41.279999 24,41.279999z"
+        android:fillColor="?attr/wallpaperTextColor"/>
+</vector>
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 12f75bb..0219db3 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -307,8 +307,9 @@
 
     void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
                                                    int removedCount, int addedCount) {
-        if (AccessibilityManager.getInstance(mContext).isEnabled() &&
-                (isFocused() || isSelected() && isShown())) {
+        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+                    AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
+                && (isFocused() || isSelected() && isShown())) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
             event.setFromIndex(fromIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 2b48e0f..51175d1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -387,7 +387,9 @@
             }
             case MotionEvent.ACTION_HOVER_ENTER:
             case MotionEvent.ACTION_HOVER_MOVE: {
-                if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isObservedEventType(
+                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
+                        && !mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                     event.setImportantForAccessibility(true);
@@ -400,7 +402,9 @@
                 break;
             }
             case MotionEvent.ACTION_HOVER_EXIT: {
-                if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isObservedEventType(
+                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
+                        && mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                     event.setImportantForAccessibility(true);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index eef43d2..a984680 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -625,9 +625,7 @@
         @Override
         public void onTaskStackChanged() {
             if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             if (getState() != STATE_NO_PIP) {
                 boolean hasPip = false;
 
@@ -662,9 +660,7 @@
         @Override
         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             if (DEBUG) Log.d(TAG, "onActivityPinned()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             StackInfo stackInfo = getPinnedStackInfo();
             if (stackInfo == null) {
                 Log.w(TAG, "Cannot find pinned stack");
@@ -690,9 +686,7 @@
         @Override
         public void onPinnedActivityRestartAttempt(boolean clearedTask) {
             if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
             movePipToFullscreen();
         }
@@ -700,9 +694,7 @@
         @Override
         public void onPinnedStackAnimationEnded() {
             if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-            if (!checkCurrentUserId(mContext, DEBUG)) {
-                return;
-            }
+
             switch (getState()) {
                 case STATE_PIP_MENU:
                     showPipMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 9400fd8..663f206 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -23,14 +23,12 @@
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
 
 import android.app.ActivityManager;
-import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
@@ -89,7 +87,6 @@
 import com.android.systemui.shared.recents.view.RecentsTransition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.stackdivider.DividerView;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.ArrayList;
@@ -657,13 +654,6 @@
         // the resize mode already.
         if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
             EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
-            showRecents(
-                    false /* triggeredFromAltTab */,
-                    dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
-                    false /* animate */,
-                    true /* launchedWhileDockingTask*/,
-                    false /* fromHome */,
-                    DividerView.INVALID_RECENTS_GROW_TARGET);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index cf3cae5..2d3080b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -68,7 +68,6 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.os.BackgroundThread;
 import com.android.systemui.Dependency;
-import com.android.systemui.R;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImpl;
@@ -372,7 +371,7 @@
         if (mIam == null) return false;
 
         try {
-            return mIam.isInLockTaskMode();
+            return mIam.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e909644..991c3c8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -162,7 +162,7 @@
         Matrix matrix = new Matrix();
         int overlayColor = 0x40FFFFFF;
 
-        Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
+        Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, data.image.getConfig());
         matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2);
         c.setBitmap(picture);
         c.drawBitmap(data.image, matrix, paint);
@@ -171,7 +171,7 @@
 
         // Note, we can't use the preview for the small icon, since it is non-square
         float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
-        Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+        Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, data.image.getConfig());
         matrix.setScale(scale, scale);
         matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
                 (iconSize - (scale * mImageHeight)) / 2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index f5c77f2..64c52ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -369,7 +369,7 @@
     private void onFacetClicked(Intent intent, int index) {
         String packageName = intent.getPackage();
 
-        if (packageName == null) {
+        if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 5c9446ce86..34486db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -250,7 +250,7 @@
                 }
                 break;
             case STATE_FACE_UNLOCK:
-                iconRes = com.android.internal.R.drawable.ic_account_circle;
+                iconRes = R.drawable.ic_account_circle;
                 break;
             case STATE_FINGERPRINT:
                 // If screen is off and device asleep, use the draw on animation so the first frame
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0d41e20..383d327 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -441,7 +441,8 @@
                 .withEndAction(() -> mDialog.dismiss())
                 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                 .start();
-        if (mAccessibilityMgr.isEnabled()) {
+        if (mAccessibilityMgr.isObservedEventType(
+                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             event.setPackageName(mContext.getPackageName());
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 32af29d..bd6af01 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5013,6 +5013,11 @@
     // OS: P
     FIELD_SELECTION_WIDGET_VERSION = 1262;
 
+    // OPEN: Settings > Battery(version 2)
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_POWER_USAGE_SUMMARY_V2 = 1263;
+
     // ---- End P Constants, all P constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 7e94d7b..22d922b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
@@ -20,9 +20,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
-import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
@@ -49,7 +47,6 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityCache;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -65,6 +62,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -106,7 +104,7 @@
 
     int mFeedbackType;
 
-    Set<String> mPackageNames = new HashSet<>();
+    final Set<String> mPackageNames = new HashSet<>();
 
     boolean mIsDefault;
 
@@ -284,40 +282,98 @@
         return true;
     }
 
-    public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
-        mEventTypes = info.eventTypes;
-        mFeedbackType = info.feedbackType;
-        String[] packageNames = info.packageNames;
-        if (packageNames != null) {
-            mPackageNames.addAll(Arrays.asList(packageNames));
+    boolean setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
+        boolean somethingChanged = false;
+
+        if (mEventTypes != info.eventTypes) {
+            mEventTypes = info.eventTypes;
+            somethingChanged = true;
         }
-        mNotificationTimeout = info.notificationTimeout;
-        mIsDefault = (info.flags & DEFAULT) != 0;
+
+        if (mFeedbackType != info.feedbackType) {
+            mFeedbackType = info.feedbackType;
+            somethingChanged = true;
+        }
+
+        final String[] oldPackageNames = mPackageNames.toArray(new String[mPackageNames.size()]);
+        if (!Arrays.equals(oldPackageNames, info.packageNames)) {
+            mPackageNames.clear();
+            if (info.packageNames != null) {
+                Collections.addAll(mPackageNames, info.packageNames);
+            }
+            somethingChanged = true;
+        }
+
+        if (mNotificationTimeout != info.notificationTimeout) {
+            mNotificationTimeout = info.notificationTimeout;
+            somethingChanged = true;
+        }
+
+        final boolean newIsDefault = (info.flags & DEFAULT) != 0;
+        if (mIsDefault != newIsDefault) {
+            mIsDefault = newIsDefault;
+            somethingChanged = true;
+        }
 
         if (supportsFlagForNotImportantViews(info)) {
-            if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
-                mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            } else {
-                mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+            somethingChanged |= updateFetchFlag(info.flags,
+                    AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
+        }
+
+        somethingChanged |= updateFetchFlag(info.flags,
+                AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS);
+
+        final boolean newRequestTouchExplorationMode = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+        if (mRequestTouchExplorationMode != newRequestTouchExplorationMode) {
+            mRequestTouchExplorationMode = newRequestTouchExplorationMode;
+            somethingChanged = true;
+        }
+
+        final boolean newRequestFilterKeyEvents = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+        if (mRequestFilterKeyEvents != newRequestFilterKeyEvents) {
+            mRequestFilterKeyEvents = newRequestFilterKeyEvents;
+            somethingChanged = true;
+        }
+
+        final boolean newRetrieveInteractiveWindows = (info.flags
+                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+        if (mRetrieveInteractiveWindows != newRetrieveInteractiveWindows) {
+            mRetrieveInteractiveWindows = newRetrieveInteractiveWindows;
+            somethingChanged = true;
+        }
+
+        final boolean newCaptureFingerprintGestures = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
+        if (mCaptureFingerprintGestures != newCaptureFingerprintGestures) {
+            mCaptureFingerprintGestures = newCaptureFingerprintGestures;
+            somethingChanged = true;
+        }
+
+        final boolean newRequestAccessibilityButton = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+        if (mRequestAccessibilityButton != newRequestAccessibilityButton) {
+            mRequestAccessibilityButton = newRequestAccessibilityButton;
+            somethingChanged = true;
+        }
+
+        return somethingChanged;
+    }
+
+    private boolean updateFetchFlag(int allFlags, int flagToUpdate) {
+        if ((allFlags & flagToUpdate) != 0) {
+            if ((mFetchFlags & flagToUpdate) == 0) {
+                mFetchFlags |= flagToUpdate;
+                return true;
+            }
+        } else {
+            if ((mFetchFlags & flagToUpdate) != 0) {
+                mFetchFlags &= ~flagToUpdate;
+                return true;
             }
         }
-
-        if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
-            mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
-        } else {
-            mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
-        }
-
-        mRequestTouchExplorationMode = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
-        mRequestFilterKeyEvents = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
-        mRetrieveInteractiveWindows = (info.flags
-                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
-        mCaptureFingerprintGestures = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
-        mRequestAccessibilityButton = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+        return false;
     }
 
     protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
@@ -349,14 +405,15 @@
                 // If the XML manifest had data to configure the service its info
                 // should be already set. In such a case update only the dynamically
                 // configurable properties.
+                final boolean serviceInfoChanged;
                 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
                 if (oldInfo != null) {
                     oldInfo.updateDynamicallyConfigurableProperties(info);
-                    setDynamicallyConfigurableProperties(oldInfo);
+                    serviceInfoChanged = setDynamicallyConfigurableProperties(oldInfo);
                 } else {
-                    setDynamicallyConfigurableProperties(info);
+                    serviceInfoChanged = setDynamicallyConfigurableProperties(info);
                 }
-                mSystemSupport.onClientChange(true);
+                mSystemSupport.onClientChange(serviceInfoChanged);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3554448..8b5c85a7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2400,7 +2400,8 @@
         private void announceNewUserIfNeeded() {
             synchronized (mLock) {
                 UserState userState = getCurrentUserStateLocked();
-                if (userState.isHandlingAccessibilityEvents()) {
+                if (userState.isHandlingAccessibilityEvents()
+                        && userState.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT)) {
                     UserManager userManager = (UserManager) mContext.getSystemService(
                             Context.USER_SERVICE);
                     String message = mContext.getString(R.string.user_switched,
@@ -3157,13 +3158,21 @@
             if (mWindowsForAccessibilityCallback == null) {
                 return;
             }
+            final int userId;
+            synchronized (mLock) {
+                userId = mCurrentUserId;
+                final UserState userState = getUserStateLocked(userId);
+                if (!userState.isObservedEventType(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) {
+                    return;
+                }
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 // Let the client know the windows changed.
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_WINDOWS_CHANGED);
                 event.setEventTime(SystemClock.uptimeMillis());
-                sendAccessibilityEvent(event, mCurrentUserId);
+                sendAccessibilityEvent(event, userId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3368,6 +3377,10 @@
             mUserId = userId;
         }
 
+        public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+            return (mLastSentRelevantEventTypes & type) != 0;
+        }
+
         public int getClientState() {
             int clientState = 0;
             final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 3419b80..62017e8 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -791,7 +791,7 @@
      */
     private void sendAccessibilityEvent(int type) {
         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-        if (accessibilityManager.isEnabled()) {
+        if (accessibilityManager.isObservedEventType(type)) {
             AccessibilityEvent event = AccessibilityEvent.obtain(type);
             event.setWindowId(mAms.getActiveWindowId());
             accessibilityManager.sendAccessibilityEvent(event);
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index c34c30c..04279a3 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -2150,31 +2150,26 @@
                                           (int)((onDuration / (1000 * 60)) % 60),
                                           (int)((onDuration / 1000) % 60),
                                           (int)(onDuration % 1000));
-                writer.println("  time since enabled: " + onDurationString + "\n");
+                writer.println("  time since enabled: " + onDurationString);
             }
 
             if (mActiveLogs.size() == 0) {
-                writer.println("Bluetooth never enabled!");
+                writer.println("\nBluetooth never enabled!");
             } else {
-                writer.println("Enable log:");
+                writer.println("\nEnable log:");
                 for (ActiveLog log : mActiveLogs) {
                     writer.println("  " + log);
                 }
             }
 
-            writer.println("Bluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
+            writer.println("\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
             if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
             for (Long time : mCrashTimestamps) {
               writer.println("  " + timeToLog(time.longValue()));
             }
 
-            String bleAppString = "No BLE Apps registered.";
-            if (mBleApps.size() == 1) {
-                bleAppString = "1 BLE App registered:";
-            } else if (mBleApps.size() > 1) {
-                bleAppString = mBleApps.size() + " BLE Apps registered:";
-            }
-            writer.println("\n" + bleAppString);
+            writer.println("\n" + mBleApps.size() + " BLE app" +
+                            (mBleApps.size() == 1 ? "" : "s") + "registered");
             for (ClientDeathRecipient app : mBleApps.values()) {
                 writer.println("  " + app.getPackageName());
             }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ced8621..bdfd82f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -501,9 +501,11 @@
             windowingMode = WINDOWING_MODE_FULLSCREEN;
         }
 
+        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
+
         // Take any required action due to us not supporting the preferred windowing mode.
         if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
-            if (display.hasSplitScreenPrimaryStack()
+            if (alreadyInSplitScreenMode
                     && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                     || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
                 // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen
@@ -577,7 +579,7 @@
                 resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
             }
         } finally {
-            if (mDisplayId == DEFAULT_DISPLAY
+            if (!alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY
                     && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 // Make sure recents stack exist when creating a dock stack as it normally needs to
                 // be on the other side of the docked stack and we make visibility decisions based
@@ -1678,12 +1680,6 @@
         return true;
     }
 
-    /** Returns true if the stack is currently considered visible. */
-    boolean isVisible() {
-        return mWindowContainerController != null && mWindowContainerController.isVisible()
-                && !mForceHidden;
-    }
-
     boolean isTopStackOnDisplay() {
         return getDisplay().isTopStack(this);
     }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 27eb985..2fc5dda 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -606,21 +606,9 @@
             return;
         }
 
-        if (startedActivityStack.inSplitScreenPrimaryWindowingMode()) {
-            final ActivityStack homeStack = mSupervisor.mHomeStack;
-            final boolean homeStackVisible = homeStack != null && homeStack.isVisible();
-            if (homeStackVisible) {
-                // We launch an activity while being in home stack, which means either launcher or
-                // recents into docked stack. We don't want the launched activity to be alone in a
-                // docked stack, so we want to immediately launch recents too.
-                if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
-                mService.mWindowManager.showRecentApps(true /* fromHome */);
-            }
-            return;
-        }
-
-        boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK) && (mReuseTask != null);
+        final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK;
+        boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags
+                && mReuseTask != null;
         if (startedActivityStack.inPinnedWindowingMode()
                 && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP
                 || clearedTask)) {
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index e87b4e6..d77e1a2 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -250,7 +250,24 @@
     }
 
     /**
-     * @return whether the requested task is allowed to be launched.
+     * @return whether the requested task is allowed to be locked (either whitelisted, or declares
+     * lockTaskMode="always" in the manifest).
+     */
+    boolean isTaskWhitelisted(TaskRecord task) {
+        switch(task.mLockTaskAuth) {
+            case LOCK_TASK_AUTH_WHITELISTED:
+            case LOCK_TASK_AUTH_LAUNCHABLE:
+            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
+                return true;
+            case LOCK_TASK_AUTH_PINNABLE:
+            case LOCK_TASK_AUTH_DONT_LOCK:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * @return whether the requested task is disallowed to be launched.
      */
     boolean isLockTaskModeViolation(TaskRecord task) {
         return isLockTaskModeViolation(task, false);
@@ -258,7 +275,7 @@
 
     /**
      * @param isNewClearTask whether the task would be cleared as part of the operation.
-     * @return whether the requested task is allowed to be launched.
+     * @return whether the requested task is disallowed to be launched.
      */
     boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
         if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
@@ -275,21 +292,18 @@
             // If the task is already at the top and won't be cleared, then allow the operation
             return false;
         }
-        final int lockTaskAuth = task.mLockTaskAuth;
-        switch (lockTaskAuth) {
-            case LOCK_TASK_AUTH_DONT_LOCK:
-                return !mLockTaskModeTasks.isEmpty();
-            case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
-            case LOCK_TASK_AUTH_LAUNCHABLE:
-            case LOCK_TASK_AUTH_WHITELISTED:
-                return false;
-            case LOCK_TASK_AUTH_PINNABLE:
-                // Pinnable tasks can't be launched on top of locktask tasks.
-                return !mLockTaskModeTasks.isEmpty();
-            default:
-                Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
-                return true;
+
+        // Allow recents activity if enabled by policy
+        if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
+            return false;
         }
+
+        return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+    }
+
+    private boolean isRecentsAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
     }
 
     /**
@@ -491,6 +505,7 @@
         }
 
         if (mLockTaskModeTasks.isEmpty()) {
+            mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
             // Start lock task on the handler thread
             mHandler.post(() -> performStartLockTask(
                     task.intent.getComponent().getPackageName(),
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index ebcf8c2..abb296e 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -502,6 +502,18 @@
         }
     }
 
+    void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
+        if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
+            return;
+        }
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (tr.userId == userId && !mService.mLockTaskController.isTaskWhitelisted(tr)) {
+                remove(tr);
+            }
+        }
+    }
+
     void removeTasksByPackageName(String packageName, int userId) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final TaskRecord tr = mTasks.get(i);
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
new file mode 100644
index 0000000..41d9feb
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.NanoAppMessage;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle
+ * notification callbacks. This class implements the IContextHubClient object, and the implemented
+ * APIs must be thread-safe.
+ *
+ * @hide
+ */
+public class ContextHubClientBroker extends IContextHubClient.Stub
+        implements IBinder.DeathRecipient {
+    private static final String TAG = "ContextHubClientBroker";
+
+    /*
+     * The context of the service.
+     */
+    private final Context mContext;
+
+    /*
+     * The proxy to talk to the Context Hub HAL.
+     */
+    private final IContexthub mContextHubProxy;
+
+    /*
+     * The manager that registered this client.
+     */
+    private final ContextHubClientManager mClientManager;
+
+    /*
+     * The ID of the hub that this client is attached to.
+     */
+    private final int mAttachedContextHubId;
+
+    /*
+     * The host end point ID of this client.
+     */
+    private final short mHostEndPointId;
+
+    /*
+     * The remote callback interface for this client.
+     */
+    private final IContextHubClientCallback mCallbackInterface;
+
+    /*
+     * false if the connection has been closed by the client, true otherwise.
+     */
+    private final AtomicBoolean mConnectionOpen = new AtomicBoolean(true);
+
+    /* package */ ContextHubClientBroker(
+            Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
+            int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+        mContext = context;
+        mContextHubProxy = contextHubProxy;
+        mClientManager = clientManager;
+        mAttachedContextHubId = contextHubId;
+        mHostEndPointId = hostEndPointId;
+        mCallbackInterface = callback;
+    }
+
+    /**
+     * Attaches a death recipient for this client
+     *
+     * @throws RemoteException if the client has already died
+     */
+    /* package */ void attachDeathRecipient() throws RemoteException {
+        mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+    }
+
+    /**
+     * Sends from this client to a nanoapp.
+     *
+     * @param message the message to send
+     * @return the error code of sending the message
+     */
+    @ContextHubTransaction.Result
+    @Override
+    public int sendMessageToNanoApp(NanoAppMessage message) {
+        ContextHubServiceUtil.checkPermissions(mContext);
+
+        int result;
+        if (mConnectionOpen.get()) {
+            ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
+                    mHostEndPointId, message);
+
+            try {
+                result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
+                        + mAttachedContextHubId + ")", e);
+                result = Result.UNKNOWN_FAILURE;
+            }
+        } else {
+            Log.e(TAG, "Failed to send message to nanoapp: client connection is closed");
+            result = Result.UNKNOWN_FAILURE;
+        }
+
+        return ContextHubServiceUtil.toTransactionResult(result);
+    }
+
+    /**
+     * Closes the connection for this client with the service.
+     */
+    @Override
+    public void close() {
+        if (mConnectionOpen.getAndSet(false)) {
+            mClientManager.unregisterClient(mHostEndPointId);
+        }
+    }
+
+    /**
+     * Invoked when the underlying binder of this broker has died at the client process.
+     */
+    public void binderDied() {
+        try {
+            IContextHubClient.Stub.asInterface(this).close();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException while closing client on death", e);
+        }
+    }
+
+    /**
+     * @return the ID of the context hub this client is attached to
+     */
+    /* package */ int getAttachedContextHubId() {
+        return mAttachedContextHubId;
+    }
+
+    /**
+     * @return the host endpoint ID of this client
+     */
+    /* package */ short getHostEndPointId() {
+        return mHostEndPointId;
+    }
+
+    /**
+     * Sends a message to the client associated with this object.
+     *
+     * @param message the message that came from a nanoapp
+     */
+    /* package */ void sendMessageToClient(NanoAppMessage message) {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onMessageFromNanoApp(message);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while sending message to client (host endpoint ID = "
+                        + mHostEndPointId + ")", e);
+            }
+        }
+    }
+
+    /**
+     * Handles a nanoapp load event.
+     *
+     * @param nanoAppId the ID of the nanoapp that was loaded.
+     */
+    /* package */ void onNanoAppLoaded(long nanoAppId) {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onNanoAppLoaded(nanoAppId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onNanoAppLoaded on client"
+                        + " (host endpoint ID = " + mHostEndPointId + ")", e);
+            }
+        }
+    }
+
+    /**
+     * Handles a nanoapp unload event.
+     *
+     * @param nanoAppId the ID of the nanoapp that was unloaded.
+     */
+    /* package */ void onNanoAppUnloaded(long nanoAppId) {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onNanoAppUnloaded(nanoAppId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onNanoAppUnloaded on client"
+                        + " (host endpoint ID = " + mHostEndPointId + ")", e);
+            }
+        }
+    }
+
+    /**
+     * Handles a hub reset for this client.
+     */
+    /* package */ void onHubReset() {
+        if (mConnectionOpen.get()) {
+            try {
+                mCallbackInterface.onHubReset();
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling onHubReset on client" +
+                        " (host endpoint ID = " + mHostEndPointId + ")", e);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
new file mode 100644
index 0000000..d58a746
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.NanoAppMessage;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * A class that manages registration/unregistration of clients and manages messages to/from clients.
+ *
+ * @hide
+ */
+/* package */ class ContextHubClientManager {
+    private static final String TAG = "ContextHubClientManager";
+
+    /*
+     * The maximum host endpoint ID value that a client can be assigned.
+     */
+    private static final int MAX_CLIENT_ID = 0x7fff;
+
+    /*
+     * Local flag to enable debug logging.
+     */
+    private static final boolean DEBUG_LOG_ENABLED = true;
+
+    /*
+     * The context of the service.
+     */
+    private final Context mContext;
+
+    /*
+     * The proxy to talk to the Context Hub.
+     */
+    private final IContexthub mContextHubProxy;
+
+    /*
+     * A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients.
+     * A concurrent data structure is used since the registration/unregistration can occur in
+     * multiple threads.
+     */
+    private final ConcurrentHashMap<Short, ContextHubClientBroker> mHostEndPointIdToClientMap =
+            new ConcurrentHashMap<>();
+
+    /*
+     * The next host endpoint ID to start iterating for the next available host endpoint ID.
+     */
+    private int mNextHostEndpointId = 0;
+
+    /* package */ ContextHubClientManager(
+            Context context, IContexthub contextHubProxy) {
+        mContext = context;
+        mContextHubProxy = contextHubProxy;
+    }
+
+    /**
+     * Registers a new client with the service.
+     *
+     * @param clientCallback the callback interface of the client to register
+     * @param contextHubId   the ID of the hub this client is attached to
+     *
+     * @return the client interface
+     *
+     * @throws IllegalStateException if max number of clients have already registered
+     */
+    /* package */ IContextHubClient registerClient(
+            IContextHubClientCallback clientCallback, int contextHubId) {
+        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+
+        try {
+            broker.attachDeathRecipient();
+        } catch (RemoteException e) {
+            // The client process has died, so we close the connection and return null.
+            Log.e(TAG, "Failed to attach death recipient to client");
+            broker.close();
+            return null;
+        }
+
+        Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
+        return IContextHubClient.Stub.asInterface(broker);
+    }
+
+    /**
+     * Handles a message sent from a nanoapp.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp sent the message from
+     * @param message      the message send by a nanoapp
+     */
+    /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) {
+        NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
+
+        if (DEBUG_LOG_ENABLED) {
+            String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast";
+            Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x"
+                    + Long.toHexString(clientMessage.getNanoAppId()));
+        }
+
+        if (clientMessage.isBroadcastMessage()) {
+            broadcastMessage(contextHubId, clientMessage);
+        } else {
+            ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint);
+            if (proxy != null) {
+                proxy.sendMessageToClient(clientMessage);
+            } else {
+                Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
+                        + message.hostEndPoint + ")");
+            }
+        }
+    }
+
+    /**
+     * Unregisters a client from the service.
+     *
+     * This method should be invoked as a result of a client calling the ContextHubClient.close(),
+     * or if the client process has died.
+     *
+     * @param hostEndPointId the host endpoint ID of the client that has died
+     */
+    /* package */ void unregisterClient(short hostEndPointId) {
+        if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) {
+            Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId);
+        } else {
+            Log.e(TAG, "Cannot unregister non-existing client with host endpoint ID "
+                    + hostEndPointId);
+        }
+    }
+
+    /**
+     * Handles a nanoapp load event.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp was loaded.
+     * @param nanoAppId    the ID of the nanoapp that was loaded.
+     */
+    /* package */ void onNanoAppLoaded(int contextHubId, long nanoAppId) {
+        forEachClientOfHub(contextHubId, client -> client.onNanoAppLoaded(nanoAppId));
+    }
+
+    /**
+     * Handles a nanoapp unload event.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp was unloaded.
+     * @param nanoAppId    the ID of the nanoapp that was unloaded.
+     */
+    /* package */ void onNanoAppUnloaded(int contextHubId, long nanoAppId) {
+        forEachClientOfHub(contextHubId, client -> client.onNanoAppUnloaded(nanoAppId));
+    }
+
+    /**
+     * Handles a hub reset.
+     *
+     * @param contextHubId the ID of the hub that has reset.
+     */
+    /* package */ void onHubReset(int contextHubId) {
+        forEachClientOfHub(contextHubId, client -> client.onHubReset());
+    }
+
+    /**
+     * Creates a new ContextHubClientBroker object for a client and registers it with the client
+     * manager.
+     *
+     * @param clientCallback the callback interface of the client to register
+     * @param contextHubId   the ID of the hub this client is attached to
+     *
+     * @return the ContextHubClientBroker object
+     *
+     * @throws IllegalStateException if max number of clients have already registered
+     */
+    private synchronized ContextHubClientBroker createNewClientBroker(
+            IContextHubClientCallback clientCallback, int contextHubId) {
+        if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
+            throw new IllegalStateException("Could not register client - max limit exceeded");
+        }
+
+        ContextHubClientBroker broker = null;
+        int id = mNextHostEndpointId;
+        for (int i = 0; i <= MAX_CLIENT_ID; i++) {
+            if (!mHostEndPointIdToClientMap.containsKey(id)) {
+                broker = new ContextHubClientBroker(
+                        mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
+                mHostEndPointIdToClientMap.put((short)id, broker);
+                mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+                break;
+            }
+
+            id = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+        }
+
+        return broker;
+    }
+
+    /**
+     * Broadcasts a message from a nanoapp to all clients attached to the associated hub.
+     *
+     * @param contextHubId the ID of the hub where the nanoapp sent the message from
+     * @param message      the message send by a nanoapp
+     */
+    private void broadcastMessage(int contextHubId, NanoAppMessage message) {
+        forEachClientOfHub(contextHubId, client -> client.sendMessageToClient(message));
+    }
+
+    /**
+     * Runs a command for each client that is attached to a hub with the given ID.
+     *
+     * @param contextHubId the ID of the hub
+     * @param callback     the command to invoke for the client
+     */
+    private void forEachClientOfHub(int contextHubId, Consumer<ContextHubClientBroker> callback) {
+        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+            if (broker.getAttachedContextHubId() == contextHubId) {
+                callback.accept(broker);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index da481a8..e08c659 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -16,12 +16,10 @@
 
 package com.android.server.location;
 
-import android.Manifest;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.HostEndPoint;
 import android.hardware.contexthub.V1_0.HubAppInfo;
 import android.hardware.contexthub.V1_0.IContexthub;
 import android.hardware.contexthub.V1_0.IContexthubCallback;
@@ -29,13 +27,17 @@
 import android.hardware.contexthub.V1_0.TransactionResult;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubMessage;
+import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubCallback;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.IContextHubService;
 import android.hardware.location.IContextHubTransactionCallback;
 import android.hardware.location.NanoApp;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppFilter;
 import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -49,7 +51,9 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -58,9 +62,6 @@
  */
 public class ContextHubService extends IContextHubService.Stub {
     private static final String TAG = "ContextHubService";
-    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
-    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
-            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
     /*
      * Constants for the type of transaction that is defined by ContextHubService.
@@ -104,6 +105,12 @@
     // The manager for transaction queue
     private final ContextHubTransactionManager mTransactionManager;
 
+    // The manager for sending messages to/from clients
+    private final ContextHubClientManager mClientManager;
+
+    // The default client for old API clients
+    private final Map<Integer, IContextHubClient> mDefaultClientMap;
+
     /**
      * Class extending the callback to register with a Context Hub.
      */
@@ -146,21 +153,34 @@
         mContextHubProxy = getContextHubProxy();
         if (mContextHubProxy == null) {
             mTransactionManager = null;
+            mClientManager = null;
+            mDefaultClientMap = Collections.EMPTY_MAP;
             mContextHubInfo = new ContextHubInfo[0];
             return;
         }
 
-        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+        mClientManager = new ContextHubClientManager(mContext, mContextHubProxy);
+        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager);
 
         List<ContextHub> hubList;
         try {
             hubList = mContextHubProxy.getHubs();
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException while getting Context Hub info");
+            Log.e(TAG, "RemoteException while getting Context Hub info", e);
             hubList = Collections.emptyList();
         }
         mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
 
+        HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
+        for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+            int contextHubId = contextHubInfo.getId();
+
+            IContextHubClient client = mClientManager.registerClient(
+                    createDefaultClientCallback(contextHubId), contextHubId);
+            defaultClientMap.put(contextHubId, client);
+        }
+        mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
+
         for (ContextHubInfo contextHubInfo : mContextHubInfo) {
             int contextHubId = contextHubInfo.getId();
             try {
@@ -168,7 +188,7 @@
                         contextHubId, new ContextHubServiceCallback(contextHubId));
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
-                        + contextHubId + ")");
+                        + contextHubId + ")", e);
             }
         }
 
@@ -185,6 +205,53 @@
     }
 
     /**
+     * Creates a default client callback for old API clients.
+     *
+     * @param contextHubId the ID of the hub to attach this client to
+     * @return the internal callback interface
+     */
+    private IContextHubClientCallback createDefaultClientCallback(int contextHubId) {
+        return new IContextHubClientCallback.Stub() {
+            @Override
+            public void onMessageFromNanoApp(NanoAppMessage message) {
+                int nanoAppInstanceId =
+                        mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ?
+                        mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1;
+
+                onMessageReceiptOldApi(
+                        message.getMessageType(), contextHubId, nanoAppInstanceId,
+                        message.getMessageBody());
+            }
+
+            @Override
+            public void onHubReset() {
+                byte[] data = {TransactionResult.SUCCESS};
+                onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+            }
+
+            @Override
+            public void onNanoAppAborted(long nanoAppId, int abortCode) {
+            }
+
+            @Override
+            public void onNanoAppLoaded(long nanoAppId) {
+            }
+
+            @Override
+            public void onNanoAppUnloaded(long nanoAppId) {
+            }
+
+            @Override
+            public void onNanoAppEnabled(long nanoAppId) {
+            }
+
+            @Override
+            public void onNanoAppDisabled(long nanoAppId) {
+            }
+        };
+    }
+
+    /**
      * @return the IContexthub proxy interface
      */
     private IContexthub getContextHubProxy() {
@@ -192,7 +259,7 @@
         try {
             proxy = IContexthub.getService(true /* retry */);
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+            Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e);
         } catch (NoSuchElementException e) {
             Log.i(TAG, "Context Hub HAL service not found");
         }
@@ -204,6 +271,7 @@
     public int registerCallback(IContextHubCallback callback) throws RemoteException {
         checkPermissions();
         mCallbacksList.register(callback);
+
         Log.d(TAG, "Added callback, total callbacks " +
                 mCallbacksList.getRegisteredCallbackCount());
         return 0;
@@ -292,7 +360,7 @@
             @Override
             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
                 byte[] data = {(byte) result};
-                onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+                onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
             }
         };
     }
@@ -449,41 +517,37 @@
             return -1;
         }
         if (msg.getData() == null) {
-            Log.w(TAG, "ContextHubMessage message body cannot be null");
+            Log.e(TAG, "ContextHubMessage message body cannot be null");
+            return -1;
+        }
+        if (!mDefaultClientMap.containsKey(hubHandle)) {
+            Log.e(TAG, "Hub with ID " + hubHandle + " does not exist");
             return -1;
         }
 
-        int result;
+        boolean success = false;
         if (nanoAppHandle == OS_APP_INSTANCE) {
             if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
-                result = queryNanoAppsInternal(hubHandle);
+                success = (queryNanoAppsInternal(hubHandle) == Result.OK);
             } else {
                 Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
-                result = Result.BAD_PARAMS;
             }
         } else {
             NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
             if (info != null) {
-                ContextHubMsg hubMessage = new ContextHubMsg();
-                hubMessage.appName = info.getAppId();
-                hubMessage.msgType = msg.getMsgType();
-                hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
-                ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
+                NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(
+                        info.getAppId(), msg.getMsgType(), msg.getData());
 
-                try {
-                    result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to send nanoapp message - RemoteException");
-                    result = Result.UNKNOWN_FAILURE;
-                }
+                IContextHubClient client = mDefaultClientMap.get(hubHandle);
+                success = (client.sendMessageToNanoApp(message) ==
+                        ContextHubTransaction.TRANSACTION_SUCCESS);
             } else {
                 Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
                         + nanoAppHandle + " does not exist.");
-                result = Result.BAD_PARAMS;
             }
         }
 
-        return (result == Result.OK ? 0 : -1);
+        return success ? 0 : -1;
     }
 
     /**
@@ -493,12 +557,7 @@
      * @param message      the message contents
      */
     private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
-        // TODO(b/67734082): Send to new API clients
-        byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
-
-        int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
-                mNanoAppIdToInstanceMap.get(message.appName) : -1;
-        onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+        mClientManager.onMessageFromNanoApp(contextHubId, message);
     }
 
     /**
@@ -534,7 +593,7 @@
         data[0] = (byte) result;
         ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
 
-        onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+        onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
     }
 
     /**
@@ -552,7 +611,7 @@
 
         byte[] data = new byte[1];
         data[0] = (byte) result;
-        onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+        onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
     }
 
     /**
@@ -577,8 +636,7 @@
             mTransactionManager.onHubReset();
             queryNanoAppsInternal(contextHubId);
 
-            byte[] data = {TransactionResult.SUCCESS};
-            onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+            mClientManager.onHubReset(contextHubId);
         } else {
             Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
                     + eventType + ")");
@@ -641,6 +699,45 @@
         }
     }
 
+    /**
+     * @param contextHubId the hub ID to validate
+     * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
+     */
+    private boolean isValidContextHubId(int contextHubId) {
+        for (ContextHubInfo hubInfo : mContextHubInfo) {
+            if (hubInfo.getId() == contextHubId) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Creates and registers a client at the service for the specified Context Hub.
+     *
+     * @param clientCallback the client interface to register with the service
+     * @param contextHubId   the ID of the hub this client is attached to
+     * @return the generated client interface, null if registration was unsuccessful
+     *
+     * @throws IllegalArgumentException if contextHubId is not a valid ID
+     * @throws IllegalStateException if max number of clients have already registered
+     * @throws NullPointerException if clientCallback is null
+     */
+    @Override
+    public IContextHubClient createClient(
+            IContextHubClientCallback clientCallback, int contextHubId) throws RemoteException {
+        checkPermissions();
+        if (!isValidContextHubId(contextHubId)) {
+            throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
+        }
+        if (clientCallback == null) {
+            throw new NullPointerException("Cannot register client with null callback");
+        }
+
+        return mClientManager.registerClient(clientCallback, contextHubId);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -664,10 +761,10 @@
     }
 
     private void checkPermissions() {
-        mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+        ContextHubServiceUtil.checkPermissions(mContext);
     }
 
-    private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+    private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) {
         if (data == null) {
             return -1;
         }
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
index ddbaf86..6faeb72 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -16,11 +16,15 @@
 
 package com.android.server.location;
 
+import android.Manifest;
+import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.HostEndPoint;
 import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.Result;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppBinary;
 import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
@@ -34,6 +38,9 @@
  */
 /* package */ class ContextHubServiceUtil {
     private static final String TAG = "ContextHubServiceUtil";
+    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
     /**
      * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
@@ -165,4 +172,40 @@
                 message.appName, message.msgType, messageArray,
                 message.hostEndPoint == HostEndPoint.BROADCAST);
     }
+
+    /**
+     * Checks for location hardware permissions.
+     *
+     * @param context the context of the service
+     */
+    /* package */
+    static void checkPermissions(Context context) {
+        context.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+    }
+
+    /**
+     * Helper function to convert from the HAL Result enum error code to the
+     * ContextHubTransaction.Result type.
+     *
+     * @param halResult the Result enum error code
+     * @return the ContextHubTransaction.Result equivalent
+     */
+    @ContextHubTransaction.Result
+    /* package */
+    static int toTransactionResult(int halResult) {
+        switch (halResult) {
+            case Result.OK:
+                return ContextHubTransaction.TRANSACTION_SUCCESS;
+            case Result.BAD_PARAMS:
+                return ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS;
+            case Result.NOT_INIT:
+                return ContextHubTransaction.TRANSACTION_FAILED_UNINITIALIZED;
+            case Result.TRANSACTION_PENDING:
+                return ContextHubTransaction.TRANSACTION_FAILED_PENDING;
+            case Result.TRANSACTION_FAILED:
+            case Result.UNKNOWN_FAILURE:
+            default: /* fall through */
+                return ContextHubTransaction.TRANSACTION_FAILED_UNKNOWN;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index 898b76c..47d9d56 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -58,6 +58,11 @@
     private final IContexthub mContextHubProxy;
 
     /*
+     * The manager for all clients for the service.
+     */
+    private final ContextHubClientManager mClientManager;
+
+    /*
      * A queue containing the current transactions
      */
     private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
@@ -73,8 +78,10 @@
     private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
     private ScheduledFuture<?> mTimeoutFuture = null;
 
-    /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+    /* package */ ContextHubTransactionManager(
+            IContexthub contextHubProxy, ContextHubClientManager clientManager) {
         mContextHubProxy = contextHubProxy;
+        mClientManager = clientManager;
     }
 
     /**
@@ -113,6 +120,9 @@
             /* package */ void onTransactionComplete(int result) {
                 try {
                     onCompleteCallback.onTransactionComplete(result);
+                    if (result == Result.OK) {
+                        mClientManager.onNanoAppLoaded(contextHubId, nanoAppBinary.getNanoAppId());
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while calling client onTransactionComplete");
                 }
@@ -153,6 +163,9 @@
             /* package */ void onTransactionComplete(int result) {
                 try {
                     onCompleteCallback.onTransactionComplete(result);
+                    if (result == Result.OK) {
+                        mClientManager.onNanoAppUnloaded(contextHubId, nanoAppId);
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while calling client onTransactionComplete");
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cf01400..bec6fc2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4743,7 +4743,8 @@
     }
 
     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
-        if (!mAccessibilityManager.isEnabled()) {
+        if (!mAccessibilityManager.isObservedEventType(
+                AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index fb6278a..20ec9b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -571,7 +571,7 @@
             if (!match) {
                 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                         "Package " + packageName +
-                        " signatures don't match previously installed version; ignoring!");
+                        " signatures do not match previously installed version; ignoring!");
             }
         }
         // Check for shared user signatures
@@ -579,16 +579,16 @@
             // Already existing package. Make sure signatures match
             boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
                     parsedSignatures) == PackageManager.SIGNATURE_MATCH;
-            if (!match) {
+            if (!match && compareCompat) {
                 match = matchSignaturesCompat(
                         packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
             }
-            if (!match && compareCompat) {
+            if (!match && compareRecover) {
                 match = matchSignaturesRecover(
                         packageName, pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures);
                 compatMatch |= match;
             }
-            if (!match && compareRecover) {
+            if (!match) {
                 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                         "Package " + packageName
                         + " has no signatures that match those in shared user "
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index b4300a9..d118c4e 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -1,3 +1,4 @@
 michaelwr@google.com
 
 per-file BatterySaverPolicy.java=omakoto@google.com
+per-file ShutdownThread.java=fkupolov@google.com
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 6bf725e..6fb345b 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -20,10 +20,7 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.IActivityManager;
-import android.app.KeyguardManager;
 import android.app.ProgressDialog;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetoothManager;
 import android.content.BroadcastReceiver;
@@ -31,8 +28,6 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
 import android.media.AudioAttributes;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -47,8 +42,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.Vibrator;
-import android.os.storage.IStorageManager;
-import android.os.storage.IStorageShutdownObserver;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TimingsTraceLog;
@@ -123,7 +116,6 @@
     private static String METRIC_RADIOS = "shutdown_radios";
     private static String METRIC_BT = "shutdown_bt";
     private static String METRIC_RADIO = "shutdown_radio";
-    private static String METRIC_SM = "shutdown_storage_manager";
 
     private final Object mActionDoneSync = new Object();
     private boolean mActionDone;
@@ -526,54 +518,6 @@
         shutdownTimingLog.traceEnd(); // ShutdownRadios
         metricEnded(METRIC_RADIOS);
 
-        // Shutdown StorageManagerService to ensure media is in a safe state
-        IStorageShutdownObserver observer = new IStorageShutdownObserver.Stub() {
-            public void onShutDownComplete(int statusCode) throws RemoteException {
-                Log.w(TAG, "Result code " + statusCode + " from StorageManagerService.shutdown");
-                actionDone();
-            }
-        };
-
-        Log.i(TAG, "Shutting down StorageManagerService");
-        shutdownTimingLog.traceBegin("ShutdownStorageManager");
-        metricStarted(METRIC_SM);
-
-        // Set initial variables and time out time.
-        mActionDone = false;
-        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
-        synchronized (mActionDoneSync) {
-            try {
-                final IStorageManager storageManager = IStorageManager.Stub.asInterface(
-                        ServiceManager.checkService("mount"));
-                if (storageManager != null) {
-                    storageManager.shutdown(observer);
-                } else {
-                    Log.w(TAG, "StorageManagerService unavailable for shutdown");
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "Exception during StorageManagerService shutdown", e);
-            }
-            while (!mActionDone) {
-                long delay = endShutTime - SystemClock.elapsedRealtime();
-                if (delay <= 0) {
-                    Log.w(TAG, "StorageManager shutdown wait timed out");
-                    break;
-                } else if (mRebootHasProgressBar) {
-                    int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
-                            (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
-                            MAX_SHUTDOWN_WAIT_TIME);
-                    status += RADIO_STOP_PERCENT;
-                    sInstance.setRebootProgress(status, null);
-                }
-                try {
-                    mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-        shutdownTimingLog.traceEnd(); // ShutdownStorageManager
-        metricEnded(METRIC_SM);
-
         if (mRebootHasProgressBar) {
             sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
 
@@ -585,6 +529,7 @@
         shutdownTimingLog.traceEnd(); // SystemServerShutdown
         metricEnded(METRIC_SYSTEM_SERVER);
         saveMetrics(mReboot);
+        // Remaining work will be done by init, including vold shutdown
         rebootOrShutdown(mContext, mReboot, mReason);
     }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 3772371..00c208d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.stats;
 
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -25,16 +26,22 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.net.NetworkStats;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.WifiActivityEnergyInfo;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatsLogEventWrapper;
+import android.os.SynchronousResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
@@ -52,6 +59,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Helper service for statsd (the native stats management service in cmds/statsd/).
@@ -60,6 +68,13 @@
  * @hide
  */
 public class StatsCompanionService extends IStatsCompanionService.Stub {
+    /**
+     * How long to wait on an individual subsystem to return its stats.
+     */
+    private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+
+    public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+
     static final String TAG = "StatsCompanionService";
     static final boolean DEBUG = true;
     public static final String ACTION_TRIGGER_COLLECTION =
@@ -79,6 +94,8 @@
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
     private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+    private IWifiManager mWifiManager = null;
+    private TelephonyManager mTelephony = null;
 
     public StatsCompanionService(Context context) {
         super();
@@ -389,6 +406,40 @@
         return ret;
     }
 
+    /**
+     * Helper method to extract the Parcelable controller info from a
+     * SynchronousResultReceiver.
+     */
+    private static <T extends Parcelable> T awaitControllerInfo(
+            @Nullable SynchronousResultReceiver receiver) {
+        if (receiver == null) {
+            return null;
+        }
+
+        try {
+            final SynchronousResultReceiver.Result result =
+                    receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
+            if (result.bundle != null) {
+                // This is the final destination for the Bundle.
+                result.bundle.setDefusable(true);
+
+                final T data = result.bundle.getParcelable(
+                        RESULT_RECEIVER_CONTROLLER_KEY);
+                if (data != null) {
+                    return data;
+                }
+            }
+            Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
+        } catch (TimeoutException e) {
+            Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
+        }
+        return null;
+    }
+
+    /**
+     *
+     * Pulls wifi controller activity energy info from WiFiManager
+     */
     @Override // Binder call
     public StatsLogEventWrapper[] pullData(int tagId) {
         enforceCallingPermission();
@@ -509,6 +560,59 @@
                 }
                 return ret.toArray(new StatsLogEventWrapper[ret.size()]);
             }
+            case StatsLog.WIFI_ACTIVITY_ENERGY_INFO_PULLED: {
+                List<StatsLogEventWrapper> ret = new ArrayList();
+                long token = Binder.clearCallingIdentity();
+                if (mWifiManager == null) {
+                    mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService(
+                            Context.WIFI_SERVICE));
+                }
+                if (mWifiManager != null) {
+                    try {
+                        SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+                        mWifiManager.requestActivityInfo(wifiReceiver);
+                        final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+                        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+                        e.writeLong(wifiInfo.getTimeStamp());
+                        e.writeInt(wifiInfo.getStackState());
+                        e.writeLong(wifiInfo.getControllerTxTimeMillis());
+                        e.writeLong(wifiInfo.getControllerRxTimeMillis());
+                        e.writeLong(wifiInfo.getControllerIdleTimeMillis());
+                        e.writeLong(wifiInfo.getControllerEnergyUsed());
+                        ret.add(e);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+                break;
+            }
+            case StatsLog.MODEM_ACTIVITY_INFO_PULLED: {
+                List<StatsLogEventWrapper> ret = new ArrayList();
+                long token = Binder.clearCallingIdentity();
+                if (mTelephony == null) {
+                    mTelephony = TelephonyManager.from(mContext);
+                }
+                if (mTelephony != null) {
+                    SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
+                    mTelephony.requestModemActivityInfo(modemReceiver);
+                    final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+                    e.writeLong(modemInfo.getTimestamp());
+                    e.writeLong(modemInfo.getSleepTimeMillis());
+                    e.writeLong(modemInfo.getIdleTimeMillis());
+                    e.writeLong(modemInfo.getTxTimeMillis()[0]);
+                    e.writeLong(modemInfo.getTxTimeMillis()[1]);
+                    e.writeLong(modemInfo.getTxTimeMillis()[2]);
+                    e.writeLong(modemInfo.getTxTimeMillis()[3]);
+                    e.writeLong(modemInfo.getTxTimeMillis()[4]);
+                    e.writeLong(modemInfo.getRxTimeMillis());
+                    e.writeLong(modemInfo.getEnergyUsed());
+                    ret.add(e);
+                }
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
@@ -523,6 +627,18 @@
         sayHiToStatsd(); // tell statsd that we're ready too and link to it
     }
 
+    @Override
+    public void triggerUidSnapshot() {
+      enforceCallingPermission();
+      synchronized (sStatsdLock) {
+        try {
+          informAllUidsLocked(mContext);
+        } catch (RemoteException e) {
+          Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+        }
+      }
+    }
+
     private void enforceCallingPermission() {
         if (Binder.getCallingPid() == Process.myPid()) {
             return;
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index c2a4be5..e7547bf 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -87,12 +87,6 @@
         }
     }
 
-    public boolean isVisible() {
-        synchronized (mWindowMap) {
-            return mContainer != null && mContainer.isVisible();
-        }
-    }
-
     public void reparent(int displayId, Rect outStackBounds, boolean onTop) {
         synchronized (mWindowMap) {
             if (mContainer == null) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 43a0893..84e475a 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -226,7 +226,7 @@
         final Rect taskFrame = new Rect();
         task.getBounds(taskFrame);
 
-        final GraphicBuffer buffer = SurfaceControl.captureLayers(
+        final GraphicBuffer buffer = SurfaceControl.captureLayersToBuffer(
                 task.getSurfaceControl().getHandle(), taskFrame, scaleFraction);
 
         if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
new file mode 100644
index 0000000..dddff8f
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.app.admin.IDevicePolicyManager;
+
+import com.android.internal.R;
+import com.android.server.SystemService;
+
+/**
+ * Defines the required interface for IDevicePolicyManager implemenation.
+ *
+ * <p>The interface consists of public parts determined by {@link IDevicePolicyManager} and also
+ * several package private methods required by internal infrastructure.
+ *
+ * <p>Whenever adding an AIDL method to {@link IDevicePolicyManager}, an empty override method
+ * should be added here to avoid build breakage in downstream branches.
+ */
+abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
+    /**
+     * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
+     *
+     * @see {@link SystemService#onBootPhase}.
+     */
+    abstract void systemReady(int phase);
+    /**
+     * To be called by {@link DevicePolicyManagerService#Lifecycle} when a new user starts.
+     *
+     * @see {@link SystemService#onStartUser}
+     */
+    abstract void handleStartUser(int userId);
+    /**
+     * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being unlocked.
+     *
+     * @see {@link SystemService#onUnlockUser}
+     */
+    abstract void handleUnlockUser(int userId);
+    /**
+     * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped.
+     *
+     * @see {@link SystemService#onStopUser}
+     */
+    abstract void handleStopUser(int userId);
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 60c36d1..223778f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -198,6 +198,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.IllegalStateException;
+import java.lang.reflect.Constructor;
 import java.nio.charset.StandardCharsets;
 import java.text.DateFormat;
 import java.util.ArrayList;
@@ -214,7 +216,7 @@
 /**
  * Implementation of the device policy APIs.
  */
-public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
 
     protected static final String LOG_TAG = "DevicePolicyManager";
 
@@ -451,11 +453,24 @@
     };
 
     public static final class Lifecycle extends SystemService {
-        private DevicePolicyManagerService mService;
+        private BaseIDevicePolicyManager mService;
 
         public Lifecycle(Context context) {
             super(context);
-            mService = new DevicePolicyManagerService(context);
+            String dpmsClassName = context.getResources()
+                    .getString(R.string.config_deviceSpecificDevicePolicyManagerService);
+            if (TextUtils.isEmpty(dpmsClassName)) {
+                dpmsClassName = DevicePolicyManagerService.class.getName();
+            }
+            try {
+                Class serviceClass = Class.forName(dpmsClassName);
+                Constructor constructor = serviceClass.getConstructor(Context.class);
+                mService = (BaseIDevicePolicyManager) constructor.newInstance(context);
+            } catch (Exception e) {
+                throw new IllegalStateException(
+                    "Failed to instantiate DevicePolicyManagerService with class name: "
+                    + dpmsClassName, e);
+            }
         }
 
         @Override
@@ -1551,6 +1566,10 @@
             mContext = context;
         }
 
+        public boolean hasFeature() {
+            return getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
+        }
+
         Context createContextAsUser(UserHandle user) throws PackageManager.NameNotFoundException {
             final String packageName = mContext.getPackageName();
             return mContext.createPackageContextAsUser(packageName, 0, user);
@@ -1848,8 +1867,7 @@
         // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false?
         mSecurityLogMonitor = new SecurityLogMonitor(this);
 
-        mHasFeature = mInjector.getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
+        mHasFeature = mInjector.hasFeature();
         mIsWatch = mInjector.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_WATCH);
         mBackgroundHandler = BackgroundThread.getHandler();
@@ -3033,6 +3051,7 @@
     }
 
     @VisibleForTesting
+    @Override
     void systemReady(int phase) {
         if (!mHasFeature) {
             return;
@@ -3099,6 +3118,7 @@
         }
     }
 
+    @Override
     void handleStartUser(int userId) {
         updateScreenCaptureDisabledInWindowManager(userId,
                 getScreenCaptureDisabled(null, userId));
@@ -3107,10 +3127,12 @@
         startOwnerService(userId, "start-user");
     }
 
+    @Override
     void handleUnlockUser(int userId) {
         startOwnerService(userId, "unlock-user");
     }
 
+    @Override
     void handleStopUser(int userId) {
         stopOwnerService(userId, "stop-user");
     }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index f093d57..fbcccf0 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -126,6 +126,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -176,6 +177,7 @@
     "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
  * </code></pre>
  */
+@Ignore
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class NetworkPolicyManagerServiceTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 1adfb67..54df744 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -90,6 +90,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private LockTaskNotify mLockTaskNotify;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private RecentTasks mRecentTasks;
 
     private LockTaskController mLockTaskController;
     private Context mContext;
@@ -110,9 +111,10 @@
             Looper.prepare();
         }
 
+        mSupervisor.mRecentTasks = mRecentTasks;
+
         mLockTaskController = new LockTaskController(mContext, mSupervisor,
                 new ImmediatelyExecuteHandler());
-
         mLockTaskController.setWindowManager(mWindowManager);
         mLockTaskController.mStatusBarService = mStatusBarService;
         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
@@ -601,6 +603,8 @@
                 eq(mContext.getPackageName()));
         verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
                 eq(mContext.getPackageName()));
+        // THEN recents should have been notified
+        verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
         // THEN the DO/PO should be informed about the operation
         verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
                 TEST_USER_ID);
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 062858d..ca4a210 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -188,6 +188,11 @@
     }
 
     /**
+     * Called when the feature is ready to use.
+     */
+    public abstract void onFeatureReady();
+
+    /**
      * Called when the feature is being removed and must be cleaned up.
      */
     public abstract void onFeatureRemoved();
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index e790d14..4e095e3a 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -346,6 +346,11 @@
         return null;
     }
 
+    @Override
+    public void onFeatureReady() {
+
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index a82e608..40c5181 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -36,6 +36,11 @@
     }
 
     @Override
+    public void onFeatureReady() {
+
+    }
+
+    @Override
     public void onFeatureRemoved() {
 
     }
diff --git a/telephony/java/android/telephony/ims/feature/SmsFeature.java b/telephony/java/android/telephony/ims/feature/SmsFeature.java
new file mode 100644
index 0000000..c1366db
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/SmsFeature.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import com.android.ims.internal.IImsSmsFeature;
+import com.android.ims.internal.ISmsListener;
+
+/**
+ * Base implementation of SMS over IMS functionality.
+ *
+ * @hide
+ */
+public class SmsFeature extends ImsFeature {
+  /**
+   * SMS over IMS format is 3gpp.
+   */
+  public static final int IMS_SMS_FORMAT_3GPP = 1;
+
+  /**
+   * SMS over IMS format is 3gpp2.
+   */
+  public static final int IMS_SMS_FORMAT_3GPP2 = 2;
+
+  /**
+   * Message was sent successfully.
+   */
+  public static final int SEND_STATUS_OK = 1;
+
+  /**
+   * IMS provider failed to send the message and platform should not retry falling back to sending
+   * the message using the radio.
+   */
+  public static final int SEND_STATUS_ERROR = 2;
+
+  /**
+   * IMS provider failed to send the message and platform should retry again after setting TP-RD bit
+   * to high.
+   */
+  public static final int SEND_STATUS_ERROR_RETRY = 3;
+
+  /**
+   * IMS provider failed to send the message and platform should retry falling back to sending
+   * the message using the radio.
+   */
+  public static final int SEND_STATUS_ERROR_FALLBACK = 4;
+
+  /**
+   * Message was delivered successfully.
+   */
+  public static final int DELIVER_STATUS_OK = 1;
+
+  /**
+   * Message was not delivered.
+   */
+  public static final int DELIVER_STATUS_ERROR = 2;
+
+  // Lock for feature synchronization
+  private final Object mLock = new Object();
+  private ISmsListener mSmsListener;
+
+  private final IImsSmsFeature mIImsSmsBinder = new IImsSmsFeature.Stub() {
+    @Override
+    public void registerSmsListener(ISmsListener listener) {
+      synchronized (mLock) {
+        SmsFeature.this.registerSmsListener(listener);
+      }
+    }
+
+    @Override
+    public void sendSms(int format, int messageRef, boolean retry, byte[] pdu) {
+      synchronized (mLock) {
+        SmsFeature.this.sendSms(format, messageRef, retry, pdu);
+      }
+    }
+
+    @Override
+    public void acknowledgeSms(int messageRef, int result) {
+      synchronized (mLock) {
+        SmsFeature.this.acknowledgeSms(messageRef, result);
+      }
+    }
+
+    @Override
+    public int getSmsFormat() {
+      synchronized (mLock) {
+        return SmsFeature.this.getSmsFormat();
+      }
+    }
+  };
+
+  /**
+   * Registers a listener responsible for handling tasks like delivering messages.
+
+   * @param listener listener to register.
+   *
+   * @hide
+   */
+  @SystemApi
+  public final void registerSmsListener(ISmsListener listener) {
+    synchronized (mLock) {
+      mSmsListener = listener;
+    }
+  }
+
+  /**
+   * This method will be triggered by the platform when the user attempts to send an SMS. This
+   * method should be implemented by the IMS providers to provide implementation of sending an SMS
+   * over IMS.
+   *
+   * @param format the format of the message. One of {@link #IMS_SMS_FORMAT_3GPP} or
+   *                {@link #IMS_SMS_FORMAT_3GPP2}
+   * @param messageRef the message reference.
+   * @param retry whether it is a retry of an already attempted message or not.
+   * @param pdu PDUs representing the contents of the message.
+   */
+  public void sendSms(int format, int messageRef, boolean isRetry, byte[] pdu) {
+  }
+
+  /**
+   * This method will be triggered by the platform after {@link #deliverSms(int, byte[])} has been
+   * called to deliver the result to the IMS provider. It will also be triggered after
+   * {@link #setSentSmsResult(int, int)} has been called to provide the result of the operation.
+   *
+   * @param result Should be {@link #DELIVER_STATUS_OK} if the message was delivered successfully,
+   * {@link #DELIVER_STATUS_ERROR} otherwise.
+   * @param messageRef the message reference.
+   */
+  public void acknowledgeSms(int messageRef, int result) {
+
+  }
+
+  /**
+   * This method should be triggered by the IMS providers when there is an incoming message. The
+   * platform will deliver the message to the messages database and notify the IMS provider of the
+   * result by calling {@link #acknowledgeSms(int)}.
+   *
+   * This method must not be called before {@link #onFeatureReady()} is called.
+   *
+   * @param format the format of the message.One of {@link #IMS_SMS_FORMAT_3GPP} or
+   *                {@link #IMS_SMS_FORMAT_3GPP2}
+   * @param pdu PDUs representing the contents of the message.
+   * @throws IllegalStateException if called before {@link #onFeatureReady()}
+   */
+  public final void deliverSms(int format, byte[] pdu) throws IllegalStateException {
+    // TODO: Guard against NPE/ Check if feature is ready and thrown an exception
+    // otherwise.
+    try {
+      mSmsListener.deliverSms(format, pdu);
+    } catch (RemoteException e) {
+    }
+  }
+
+  /**
+   * This method should be triggered by the IMS providers to pass the result of the sent message
+   * to the platform.
+   *
+   * This method must not be called before {@link #onFeatureReady()} is called.
+   *
+   * @param messageRef the message reference.
+   * @param result One of {@link #SEND_STATUS_OK}, {@link #SEND_STATUS_ERROR},
+   *                {@link #SEND_STATUS_ERROR_RETRY}, {@link #SEND_STATUS_ERROR_FALLBACK}
+   * @throws IllegalStateException if called before {@link #onFeatureReady()}
+   */
+  public final void setSentSmsResult(int messageRef, int result) throws IllegalStateException {
+    // TODO: Guard against NPE/ Check if feature is ready and thrown an exception
+    // otherwise.
+    try {
+      mSmsListener.setSentSmsResult(messageRef, result);
+    } catch (RemoteException e) {
+    }
+  }
+
+  /**
+   * Sets the status report of the sent message.
+   *
+   * @param format Should be {@link #IMS_SMS_FORMAT_3GPP} or {@link #IMS_SMS_FORMAT_3GPP2}
+   * @param pdu PDUs representing the content of the status report.
+   * @throws IllegalStateException if called before {@link #onFeatureReady()}
+   */
+  public final void setSentSmsStatusReport(int format, byte[] pdu) {
+    // TODO: Guard against NPE/ Check if feature is ready and thrown an exception
+    // otherwise.
+    try {
+      mSmsListener.setSentSmsStatusReport(format, pdu);
+    } catch (RemoteException e) {
+    }
+  }
+
+  /**
+   * Returns the SMS format. Default is {@link #IMS_SMS_FORMAT_3GPP} unless overridden by IMS
+   * Provider.
+   *
+   * @return sms format.
+   */
+  public int getSmsFormat() {
+    return IMS_SMS_FORMAT_3GPP;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void onFeatureReady() {
+
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void onFeatureRemoved() {
+
+  }
+
+  /**
+   * @hide
+   */
+  @Override
+  public final IImsSmsFeature getBinder() {
+    return mIImsSmsBinder;
+  }
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl b/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl
new file mode 100644
index 0000000..5068128
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsSmsFeature.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import com.android.ims.internal.ISmsListener;
+
+/**
+ * See SmsFeature for more information.
+ *
+ * {@hide}
+ */
+interface IImsSmsFeature {
+    void registerSmsListener(in ISmsListener listener);
+    void sendSms(in int format, in int messageRef, in boolean retry, in byte[] pdu);
+    void acknowledgeSms(in int messageRef, in int result);
+    int getSmsFormat();
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/ISmsListener.aidl b/telephony/java/com/android/ims/internal/ISmsListener.aidl
new file mode 100644
index 0000000..1266f04
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ISmsListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+/**
+ * See SmsFeature for more information.
+ * {@hide}
+ */
+interface ISmsListener {
+    void setSentSmsResult(in int messageRef, in int result);
+    void setSentSmsStatusReport(in int format, in byte[] pdu);
+    void deliverSms(in int format, in byte[] pdu);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
index 5f2e561..d27a758 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
@@ -18,6 +18,7 @@
 
 import android.util.SparseBooleanArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.SmsAddress;
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.util.HexDump;
@@ -113,8 +114,8 @@
      * share code and logic with GSM.  Also, gather all DTMF/BCD
      * processing code in one place.
      */
-
-    private static byte[] parseToDtmf(String address) {
+    @VisibleForTesting
+    public static byte[] parseToDtmf(String address) {
         int digits = address.length();
         byte[] result = new byte[digits];
         for (int i = 0; i < digits; i++) {
@@ -196,33 +197,46 @@
     public static CdmaSmsAddress parse(String address) {
         CdmaSmsAddress addr = new CdmaSmsAddress();
         addr.address = address;
-        addr.ton = CdmaSmsAddress.TON_UNKNOWN;
-        byte[] origBytes = null;
+        addr.ton = TON_UNKNOWN;
+        addr.digitMode = DIGIT_MODE_4BIT_DTMF;
+        addr.numberPlan = NUMBERING_PLAN_UNKNOWN;
+        addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
+
+        byte[] origBytes;
         String filteredAddr = filterNumericSugar(address);
-        if (filteredAddr != null) {
-            origBytes = parseToDtmf(filteredAddr);
-        }
-        if (origBytes != null) {
-            addr.digitMode = DIGIT_MODE_4BIT_DTMF;
-            addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
-            if (address.indexOf('+') != -1) {
-                addr.ton = TON_INTERNATIONAL_OR_IP;
-            }
-        } else {
-            filteredAddr = filterWhitespace(address);
-            origBytes = UserData.stringToAscii(filteredAddr);
-            if (origBytes == null) {
-                return null;
-            }
+        if (address.contains("+") || filteredAddr == null) {
+            // 3GPP2 C.S0015-B section 3.4.3.3 Address Parameters
+            // NUMBER_MODE should set to 1 for network address and email address.
             addr.digitMode = DIGIT_MODE_8BIT_CHAR;
             addr.numberMode = NUMBER_MODE_DATA_NETWORK;
-            if (address.indexOf('@') != -1) {
+            filteredAddr = filterWhitespace(address);
+
+            if (address.contains("@")) {
+                // This is an email address
                 addr.ton = TON_NATIONAL_OR_EMAIL;
+            } else if (address.contains("+") && filterNumericSugar(address) != null) {
+                // This is an international number
+                // 3GPP2 C.S0015-B section 3.4.3.3 Address Parameters
+                // digit mode is set to 1 and number mode is set to 0, type of number should set
+                // to the value correspond to the value in 3GPP2 C.S005-D, table2.7.1.3.2.4-2
+                addr.ton = TON_INTERNATIONAL_OR_IP;
+                addr.numberPlan = NUMBERING_PLAN_ISDN_TELEPHONY;
+                addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
+                filteredAddr = filterNumericSugar(address);
             }
+
+            origBytes = UserData.stringToAscii(filteredAddr);
+        } else {
+            // The address is not an international number and it only contains digit and *#
+            origBytes = parseToDtmf(filteredAddr);
         }
+
+        if (origBytes == null) {
+            return null;
+        }
+
         addr.origBytes = origBytes;
         addr.numberOfDigits = origBytes.length;
         return addr;
     }
-
 }
diff --git a/test-base/Android.mk b/test-base/Android.mk
index 9fc70f3..6a1ac9e 100644
--- a/test-base/Android.mk
+++ b/test-base/Android.mk
@@ -46,20 +46,6 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
-# Build the repackaged-legacy-test library
-# ========================================
-# This contains repackaged versions of the classes from legacy-test.
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE := repackaged-legacy-test
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Build the repackaged.android.test.base library
 # ==============================================
 # This contains repackaged versions of the classes from legacy-test.
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index e4af17c..2c07955 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
 
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index de4fb73..eaaefd5 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -346,30 +346,15 @@
   return true;
 }
 
-class FullyQualifiedClassNameVisitor : public xml::Visitor {
- public:
-  using xml::Visitor::Visit;
-
-  explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {}
-
-  void Visit(xml::Element* el) override {
-    for (xml::Attribute& attr : el->attributes) {
-      if (attr.namespace_uri == xml::kSchemaAndroid &&
-          class_attributes_.find(attr.name) != class_attributes_.end()) {
-        if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package_, attr.value)) {
-          attr.value = std::move(new_value.value());
-        }
-      }
+static void FullyQualifyClassName(const StringPiece& package, const StringPiece& attr_ns,
+                                  const StringPiece& attr_name, xml::Element* el) {
+  xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name);
+  if (attr != nullptr) {
+    if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package, attr->value)) {
+      attr->value = std::move(new_value.value());
     }
-
-    // Super implementation to iterate over the children.
-    xml::Visitor::Visit(el);
   }
-
- private:
-  StringPiece package_;
-  std::unordered_set<StringPiece> class_attributes_ = {"name"};
-};
+}
 
 static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) {
   xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
@@ -381,8 +366,25 @@
   std::string original_package = std::move(attr->value);
   attr->value = package_override.to_string();
 
-  FullyQualifiedClassNameVisitor visitor(original_package);
-  manifest_el->Accept(&visitor);
+  xml::Element* application_el = manifest_el->FindChild({}, "application");
+  if (application_el != nullptr) {
+    FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", application_el);
+    FullyQualifyClassName(original_package, xml::kSchemaAndroid, "backupAgent", application_el);
+
+    for (xml::Element* child_el : application_el->GetChildElements()) {
+      if (child_el->namespace_uri.empty()) {
+        if (child_el->name == "activity" || child_el->name == "activity-alias" ||
+            child_el->name == "provider" || child_el->name == "receiver" ||
+            child_el->name == "service") {
+          FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el);
+        }
+
+        if (child_el->name == "activity-alias") {
+          FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el);
+        }
+      }
+    }
+  }
   return true;
 }
 
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index da7f410..40085ea 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -240,6 +240,7 @@
   std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="android">
+        <uses-split android:name="feature_a" />
         <application android:name=".MainApplication" text="hello">
           <activity android:name=".activity.Start" />
           <receiver android:name="com.google.android.Receiver" />
@@ -248,35 +249,41 @@
                                                             options);
   ASSERT_NE(nullptr, doc);
 
-  xml::Element* manifestEl = doc->root.get();
-  ASSERT_NE(nullptr, manifestEl);
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_NE(nullptr, manifest_el);
 
   xml::Attribute* attr = nullptr;
 
-  attr = manifestEl->FindAttribute({}, "package");
+  attr = manifest_el->FindAttribute({}, "package");
   ASSERT_NE(nullptr, attr);
   EXPECT_EQ(std::string("com.android"), attr->value);
 
-  xml::Element* applicationEl = manifestEl->FindChild({}, "application");
-  ASSERT_NE(nullptr, applicationEl);
+  xml::Element* uses_split_el = manifest_el->FindChild({}, "uses-split");
+  ASSERT_NE(nullptr, uses_split_el);
+  attr = uses_split_el->FindAttribute(xml::kSchemaAndroid, "name");
+  ASSERT_NE(nullptr, attr);
+  EXPECT_EQ(std::string("feature_a"), attr->value);
 
-  attr = applicationEl->FindAttribute(xml::kSchemaAndroid, "name");
+  xml::Element* application_el = manifest_el->FindChild({}, "application");
+  ASSERT_NE(nullptr, application_el);
+
+  attr = application_el->FindAttribute(xml::kSchemaAndroid, "name");
   ASSERT_NE(nullptr, attr);
   EXPECT_EQ(std::string("android.MainApplication"), attr->value);
 
-  attr = applicationEl->FindAttribute({}, "text");
+  attr = application_el->FindAttribute({}, "text");
   ASSERT_NE(nullptr, attr);
   EXPECT_EQ(std::string("hello"), attr->value);
 
   xml::Element* el;
-  el = applicationEl->FindChild({}, "activity");
+  el = application_el->FindChild({}, "activity");
   ASSERT_NE(nullptr, el);
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "name");
   ASSERT_NE(nullptr, el);
   EXPECT_EQ(std::string("android.activity.Start"), attr->value);
 
-  el = applicationEl->FindChild({}, "receiver");
+  el = application_el->FindChild({}, "receiver");
   ASSERT_NE(nullptr, el);
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "name");