Merge "Build hidden API DEX signatures with Soong"
diff --git a/api/current.txt b/api/current.txt
index 1bf03d5..cbff777 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32755,6 +32755,7 @@
     method public java.util.ArrayList readArrayList(java.lang.ClassLoader);
     method public void readBinderArray(android.os.IBinder[]);
     method public void readBinderList(java.util.List<android.os.IBinder>);
+    method public boolean readBoolean();
     method public void readBooleanArray(boolean[]);
     method public android.os.Bundle readBundle();
     method public android.os.Bundle readBundle(java.lang.ClassLoader);
@@ -32800,6 +32801,7 @@
     method public void writeArray(java.lang.Object[]);
     method public void writeBinderArray(android.os.IBinder[]);
     method public void writeBinderList(java.util.List<android.os.IBinder>);
+    method public void writeBoolean(boolean);
     method public void writeBooleanArray(boolean[]);
     method public void writeBundle(android.os.Bundle);
     method public void writeByte(byte);
diff --git a/api/test-current.txt b/api/test-current.txt
index 50aaa0a..61b4a75 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,10 +1,13 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
     field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
+    field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
+    field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
   }
 
 }
@@ -61,9 +64,12 @@
   public class AppOpsManager {
     method public static java.lang.String[] getOpStrs();
     method public boolean isOperationActive(int, int, java.lang.String);
+    method public static java.lang.String opToPermission(int);
+    method public static int permissionToOpCode(java.lang.String);
     method public void setMode(int, int, java.lang.String, int);
     method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener);
+    method public static int strOpToOp(java.lang.String);
     field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
@@ -109,6 +115,7 @@
     field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_RECORD_AUDIO = 27; // 0x1b
     field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
+    field public static final int _NUM_OP = 77; // 0x4d
   }
 
   public static abstract interface AppOpsManager.OnOpActiveChangedListener {
@@ -222,11 +229,16 @@
   }
 
   public abstract class Context {
+    method public abstract java.lang.String getOpPackageName();
     method public android.os.UserHandle getUser();
     method public int getUserId();
     method public void setAutofillCompatibilityEnabled(boolean);
   }
 
+  public class ContextWrapper extends android.content.Context {
+    method public java.lang.String getOpPackageName();
+  }
+
 }
 
 package android.content.pm {
@@ -471,6 +483,10 @@
     method public void setType(int);
   }
 
+  public class Location implements android.os.Parcelable {
+    method public void makeComplete();
+  }
+
   public class LocationManager {
     method public java.lang.String[] getBackgroundThrottlingWhitelist();
   }
@@ -667,6 +683,7 @@
   public final class UserHandle implements android.os.Parcelable {
     method public static int getAppId(int);
     method public int getIdentifier();
+    method public static boolean isApp(int);
     field public static final android.os.UserHandle SYSTEM;
   }
 
@@ -737,6 +754,14 @@
 
 package android.provider {
 
+  public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+    field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
+  }
+
+  public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns {
+    field public static final android.net.Uri CORP_CONTENT_URI;
+  }
+
   public final class Settings {
     field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
   }
@@ -962,9 +987,11 @@
 
   public class TelephonyManager {
     method public int getCarrierIdListVersion();
+    method public void refreshUiccProfile();
     method public void setCarrierTestOverride(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
   }
+
 }
 
 package android.telephony.mbms {
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index e5d35b3..a7349ee 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -94,3 +94,5 @@
 endif
 
 include ${BUILD_SHARED_LIBRARY}
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
new file mode 100644
index 0000000..8f5cfb37
--- /dev/null
+++ b/cmds/bootanimation/iot/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(PRODUCT_IOT),true)
+
+# libbootanimation_iot_test
+# ===========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbootanimation_iot_test
+LOCAL_CFLAGS := -Wall -Werror -Wunused -Wunreachable-code
+
+LOCAL_SHARED_LIBRARIES := \
+    libandroidthings \
+    libbase \
+    libchrome \
+    liblog \
+
+LOCAL_SRC_FILES := \
+    BootParameters.cpp \
+    BootParameters_test.cpp \
+
+include $(BUILD_NATIVE_TEST)
+
+endif # PRODUCT_IOT
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
index da6ad0d..06cdbf8 100644
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -20,8 +20,6 @@
 
 #include <fcntl.h>
 
-#include <string>
-
 #include <android-base/file.h>
 #include <base/json/json_parser.h>
 #include <base/json/json_reader.h>
@@ -98,7 +96,11 @@
         return;
     }
 
-    std::unique_ptr<Value> json = JSONReader::Read(contents);
+    loadParameters(contents);
+}
+
+void BootParameters::loadParameters(const std::string& raw_json) {
+    std::unique_ptr<Value> json = JSONReader::Read(raw_json);
     if (json.get() == nullptr) {
         return;
     }
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
index c10bd44..50e5d57 100644
--- a/cmds/bootanimation/iot/BootParameters.h
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -18,6 +18,7 @@
 #define _BOOTANIMATION_BOOT_PARAMETERS_H_
 
 #include <list>
+#include <string>
 #include <vector>
 
 #include <base/json/json_value_converter.h>
@@ -43,6 +44,8 @@
     // Returns the additional boot parameters that were set on reboot.
     const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
 
+    // Exposed for testing. Updates the parameters with new JSON values.
+    void loadParameters(const std::string& raw_json);
 private:
     // Raw boot saved_parameters loaded from .json.
     struct SavedBootParameters {
diff --git a/cmds/bootanimation/iot/BootParameters_test.cpp b/cmds/bootanimation/iot/BootParameters_test.cpp
new file mode 100644
index 0000000..431563c
--- /dev/null
+++ b/cmds/bootanimation/iot/BootParameters_test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BootParameters.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace {
+
+TEST(BootParametersTest, TestParseValidParameters) {
+  BootParameters boot_parameters = BootParameters();
+  boot_parameters.loadParameters(R"(
+    {
+      "brightness":200,
+      "volume":100,
+      "param_names":["key1","key2"],
+      "param_values":["value1","value2"]
+    }
+  )");
+
+  EXPECT_TRUE(boot_parameters.hasBrightness());
+  EXPECT_TRUE(boot_parameters.hasVolume());
+  EXPECT_FLOAT_EQ(0.2f, boot_parameters.getBrightness());
+  EXPECT_FLOAT_EQ(0.1f, boot_parameters.getVolume());
+
+  auto parameters = boot_parameters.getParameters();
+  ASSERT_EQ(2u, parameters.size());
+  ASSERT_STREQ(parameters[0].key, "key1");
+  ASSERT_STREQ(parameters[0].value, "value1");
+  ASSERT_STREQ(parameters[1].key, "key2");
+  ASSERT_STREQ(parameters[1].value, "value2");
+}
+
+TEST(BootParametersTest, TestMismatchedParameters) {
+  BootParameters boot_parameters = BootParameters();
+  boot_parameters.loadParameters(R"(
+    {
+      "brightness":500,
+      "volume":500,
+      "param_names":["key1","key2"],
+      "param_values":["value1"]
+    }
+  )");
+
+  EXPECT_TRUE(boot_parameters.hasBrightness());
+  EXPECT_TRUE(boot_parameters.hasVolume());
+  EXPECT_FLOAT_EQ(0.5f, boot_parameters.getBrightness());
+  EXPECT_FLOAT_EQ(0.5f, boot_parameters.getVolume());
+
+  auto parameters = boot_parameters.getParameters();
+  ASSERT_EQ(0u, parameters.size());
+}
+
+TEST(BootParametersTest, TestMissingParameters) {
+  BootParameters boot_parameters = BootParameters();
+  boot_parameters.loadParameters(R"(
+    {
+      "brightness":500
+    }
+  )");
+
+  EXPECT_TRUE(boot_parameters.hasBrightness());
+  EXPECT_FALSE(boot_parameters.hasVolume());
+  EXPECT_FLOAT_EQ(0.5f, boot_parameters.getBrightness());
+  EXPECT_FLOAT_EQ(-1.0f, boot_parameters.getVolume());
+
+  auto parameters = boot_parameters.getParameters();
+  ASSERT_EQ(0u, parameters.size());
+}
+
+}  // namespace
+
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index e21392c..511aec3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -122,6 +122,12 @@
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
+
+void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+}
+
 void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index cafc882..8e94a75 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -59,6 +59,8 @@
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3661b31..5610492 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -438,6 +438,11 @@
     mPastBuckets.clear();
 }
 
+void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+}
+
 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                                 const bool include_current_partial_bucket,
                                                 ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 80fbdde..f755294 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -65,6 +65,8 @@
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 2f2679e..23f780a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -100,6 +100,10 @@
     return buffer;
 }
 
+void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    mProto->clear();
+}
+
 void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 5c29174..23302c4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -49,6 +49,7 @@
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1270856..005cb71 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -160,6 +160,12 @@
     }
 }
 
+void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+    mSkippedBuckets.clear();
+}
+
 void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 71d5912..8af2f36 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -91,6 +91,7 @@
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // for testing
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 532ecbf..4239649 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -119,6 +119,11 @@
         return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, protoOutput);
     }
 
+    void clearPastBuckets(const int64_t dumpTimeNs) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return clearPastBucketsLocked(dumpTimeNs);
+    }
+
     void dumpStates(FILE* out, bool verbose) const {
         std::lock_guard<std::mutex> lock(mMutex);
         dumpStatesLocked(out, verbose);
@@ -177,6 +182,7 @@
     virtual void onDumpReportLocked(const int64_t dumpTimeNs,
                                     const bool include_current_partial_bucket,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
+    virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
     virtual size_t byteSizeLocked() const = 0;
     virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 47a1a86..0d3aebf 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -201,6 +201,8 @@
                     protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
             producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, protoOutput);
             protoOutput->end(token);
+        } else {
+            producer->clearPastBuckets(dumpTimeStampNs);
         }
     }
     for (const auto& annotation : mAnnotations) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 27fd78f..506cf38 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -151,6 +151,12 @@
     mPastBuckets.clear();
 }
 
+void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+    flushIfNeededLocked(dumpTimeNs);
+    mPastBuckets.clear();
+    mSkippedBuckets.clear();
+}
+
 void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8df30d3..64604c2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -89,6 +89,7 @@
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index a1219ea..3d17ad7 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -228,10 +228,8 @@
 Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
 Landroid/app/AppOpsManager;->OP_WRITE_CONTACTS:I
 Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
-Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I
 Landroid/app/AppOpsManager;->setRestriction(III[Ljava/lang/String;)V
 Landroid/app/AppOpsManager;->sOpPerms:[Ljava/lang/String;
-Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
 Landroid/app/backup/BackupDataInput$EntityHeader;->dataSize:I
 Landroid/app/backup/BackupDataInput$EntityHeader;->key:Ljava/lang/String;
 Landroid/app/backup/BackupDataInputStream;->dataSize:I
@@ -490,6 +488,7 @@
 Landroid/bluetooth/BluetoothA2dp;->ACTION_CODEC_CONFIG_CHANGED:Ljava/lang/String;
 Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
 Landroid/bluetooth/BluetoothA2dp;->disableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
+Landroid/bluetooth/BluetoothA2dp;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
 Landroid/bluetooth/BluetoothA2dp;->enableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
 Landroid/bluetooth/BluetoothA2dp;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
 Landroid/bluetooth/BluetoothA2dp;->getCodecStatus(Landroid/bluetooth/BluetoothDevice;)Landroid/bluetooth/BluetoothCodecStatus;
@@ -804,6 +803,7 @@
 Landroid/content/pm/PackageParser$Package;->mPreferredOrder:I
 Landroid/content/pm/PackageParser$Package;->mSharedUserId:Ljava/lang/String;
 Landroid/content/pm/PackageParser$Package;->mSharedUserLabel:I
+Landroid/content/pm/PackageParser$Package;->mSigningDetails:Landroid/content/pm/PackageParser$SigningDetails;
 Landroid/content/pm/PackageParser$Package;->mVersionCode:I
 Landroid/content/pm/PackageParser$Package;->mVersionName:Ljava/lang/String;
 Landroid/content/pm/PackageParser$Package;->packageName:Ljava/lang/String;
@@ -820,6 +820,7 @@
 Landroid/content/pm/PackageParser$ProviderIntentInfo;->provider:Landroid/content/pm/PackageParser$Provider;
 Landroid/content/pm/PackageParser$Service;->info:Landroid/content/pm/ServiceInfo;
 Landroid/content/pm/PackageParser$ServiceIntentInfo;->service:Landroid/content/pm/PackageParser$Service;
+Landroid/content/pm/PackageParser$SigningDetails;->signatures:[Landroid/content/pm/Signature;
 Landroid/content/pm/PackageParser;-><init>()V
 Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
 Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
@@ -883,6 +884,7 @@
 Landroid/content/res/DrawableCache;-><init>()V
 Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
 Landroid/content/res/ObbInfo;->salt:[B
+Landroid/content/res/Resources$Theme;->mThemeImpl:Landroid/content/res/ResourcesImpl$ThemeImpl;
 Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
 Landroid/content/res/Resources;->loadXmlResourceParser(ILjava/lang/String;)Landroid/content/res/XmlResourceParser;
 Landroid/content/res/Resources;->loadXmlResourceParser(Ljava/lang/String;IILjava/lang/String;)Landroid/content/res/XmlResourceParser;
@@ -1086,8 +1088,10 @@
 Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
 Landroid/graphics/GraphicBuffer;->mNativeObject:J
 Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
+Landroid/graphics/Insets;->bottom:I
 Landroid/graphics/Insets;->left:I
 Landroid/graphics/Insets;->right:I
+Landroid/graphics/Insets;->top:I
 Landroid/graphics/LinearGradient;->mColors:[I
 Landroid/graphics/Matrix;->native_instance:J
 Landroid/graphics/Movie;-><init>(J)V
@@ -1460,6 +1464,7 @@
 Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
 Landroid/media/soundtrigger/SoundTriggerDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer;
 Landroid/media/soundtrigger/SoundTriggerDetector$EventPayload;->getData()[B
+Landroid/media/soundtrigger/SoundTriggerManager;->isRecognitionActive(Ljava/util/UUID;)Z
 Landroid/media/soundtrigger/SoundTriggerManager;->loadSoundModel(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->startRecognition(Ljava/util/UUID;Landroid/os/Bundle;Landroid/content/ComponentName;Landroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
@@ -2276,6 +2281,7 @@
 Landroid/renderscript/RenderScriptCacheDir;->mCacheDir:Ljava/io/File;
 Landroid/renderscript/RenderScriptCacheDir;->setupDiskCache(Ljava/io/File;)V
 Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J
+Landroid/security/keystore/recovery/RecoveryController;->getRecoveryStatus(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/security/keystore/recovery/RecoveryController;->initRecoveryService(Ljava/lang/String;[B)V
 Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
 Landroid/security/net/config/RootTrustManager;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
@@ -2374,6 +2380,7 @@
 Landroid/telephony/SignalStrength;->getCdmaLevel()I
 Landroid/telephony/SignalStrength;->getDbm()I
 Landroid/telephony/SignalStrength;->getGsmDbm()I
+Landroid/telephony/SignalStrength;->getLteCqi()I
 Landroid/telephony/SignalStrength;->getLteDbm()I
 Landroid/telephony/SignalStrength;->getLteRsrp()I
 Landroid/telephony/SignalStrength;->getLteRsrq()I
@@ -2964,6 +2971,7 @@
 Landroid/webkit/WebView;->getVisibleTitleHeight()I
 Landroid/webkit/WebView;->isPaused()Z
 Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView;->mWebViewThread:Landroid/os/Looper;
 Landroid/webkit/WebView;->notifyFindDialogDismissed()V
 Landroid/webkit/WebView;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V
 Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z
@@ -3175,6 +3183,7 @@
 Landroid/widget/ScrollView;->mOverflingDistance:I
 Landroid/widget/ScrollView;->mOverscrollDistance:I
 Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/SearchView$SearchAutoComplete;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 Landroid/widget/SearchView;->mCloseButton:Landroid/widget/ImageView;
 Landroid/widget/SearchView;->mSearchButton:Landroid/widget/ImageView;
 Landroid/widget/SearchView;->mSearchPlate:Landroid/view/View;
@@ -3228,6 +3237,8 @@
 Landroid/widget/VideoView;->mUri:Landroid/net/Uri;
 Landroid/widget/VideoView;->mVideoHeight:I
 Landroid/widget/VideoView;->mVideoWidth:I
+Landroid/widget/ViewAnimator;->mFirstTime:Z
+Landroid/widget/ViewAnimator;->mWhichChild:I
 Lcom/android/ims/internal/uce/common/CapInfo;-><init>()V
 Lcom/android/ims/internal/uce/common/CapInfo;->setCapTimestamp(J)V
 Lcom/android/ims/internal/uce/common/CapInfo;->setCdViaPresenceSupported(Z)V
@@ -3931,6 +3942,7 @@
 Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
 Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
 Ljava/util/HashMap$HashIterator;->hasNext()Z
+Ljava/util/HashMap$HashIterator;->remove()V
 Ljava/util/HashMap;->modCount:I
 Ljava/util/HashMap;->table:[Ljava/util/HashMap$Node;
 Ljava/util/HashSet;->map:Ljava/util/HashMap;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 4f911ac..b5cce56 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -82,7 +82,6 @@
 Landroid/content/ContentResolver;->registerContentObserver(Landroid/net/Uri;ZLandroid/database/ContentObserver;I)V
 Landroid/content/ContentValues;->getStringArrayList(Ljava/lang/String;)Ljava/util/ArrayList;
 Landroid/content/ContentValues;->putStringArrayList(Ljava/lang/String;Ljava/util/ArrayList;)V
-Landroid/content/Context;->getOpPackageName()Ljava/lang/String;
 Landroid/content/Context;->registerReceiverAsUser(Landroid/content/BroadcastReceiver;Landroid/os/UserHandle;Landroid/content/IntentFilter;Ljava/lang/String;Landroid/os/Handler;)Landroid/content/Intent;
 Landroid/content/Context;->startActivityAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)V
 Landroid/content/Context;->startServiceAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)Landroid/content/ComponentName;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7424862..2e788aa 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -317,6 +317,7 @@
     /** @hide Any app start foreground service. */
     public static final int OP_START_FOREGROUND = 76;
     /** @hide */
+    @TestApi
     public static final int _NUM_OP = 77;
 
     /** Access to coarse location information. */
@@ -1376,6 +1377,7 @@
      * Retrieve the permission associated with an operation, or null if there is not one.
      * @hide
      */
+    @TestApi
     public static String opToPermission(int op) {
         return sOpPerms[op];
     }
@@ -1394,6 +1396,7 @@
      * to the corresponding app op.
      * @hide
      */
+    @TestApi
     public static int permissionToOpCode(String permission) {
         Integer boxedOpCode = sPermToOp.get(permission);
         return boxedOpCode != null ? boxedOpCode : OP_NONE;
@@ -1571,7 +1574,7 @@
             long time = 0;
             for (int i = 0; i < _NUM_UID_STATE; i++) {
                 if (mRejectTimes[i] > time) {
-                    time = mTimes[i];
+                    time = mRejectTimes[i];
                 }
             }
             return time;
@@ -1993,6 +1996,7 @@
     /**
      * {@hide}
      */
+    @TestApi
     public static int strOpToOp(String op) {
         Integer val = sOpStrToOp.get(op);
         if (val == null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c5b8019..badb2f6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1110,11 +1110,11 @@
     public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
 
     /**
-     * {@link #extras} key: A
-     * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
-     * in the background when the notification is selected. Used on television platforms.
-     * The URI must point to an image stream suitable for passing into
-     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
+     * {@link #extras} key:
+     * flat {@link String} representation of a {@link android.content.ContentUris content URI}
+     * pointing to an image that can be displayed in the background when the notification is
+     * selected. Used on television platforms. The URI must point to an image stream suitable for
+     * passing into {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
      * BitmapFactory.decodeStream}; all other content types will be ignored.
      */
     public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
@@ -2335,7 +2335,9 @@
 
         if (extras != null) {
             visitor.accept(extras.getParcelable(EXTRA_AUDIO_CONTENTS_URI));
-            visitor.accept(extras.getParcelable(EXTRA_BACKGROUND_IMAGE_URI));
+            if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) {
+                visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI)));
+            }
         }
 
         if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) {
@@ -2722,7 +2724,40 @@
      * @hide
      */
     public static boolean areRemoteViewsChanged(Builder first, Builder second) {
-        return !first.usesStandardHeader() || !second.usesStandardHeader();
+        if (!Objects.equals(first.usesStandardHeader(), second.usesStandardHeader())) {
+            return true;
+        }
+
+        if (areRemoteViewsChanged(first.mN.contentView, second.mN.contentView)) {
+            return true;
+        }
+        if (areRemoteViewsChanged(first.mN.bigContentView, second.mN.bigContentView)) {
+            return true;
+        }
+        if (areRemoteViewsChanged(first.mN.headsUpContentView, second.mN.headsUpContentView)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static boolean areRemoteViewsChanged(RemoteViews first, RemoteViews second) {
+        if (first == null && second == null) {
+            return false;
+        }
+        if (first == null && second != null || first != null && second == null) {
+            return true;
+        }
+
+        if (!Objects.equals(first.getLayoutId(), second.getLayoutId())) {
+            return true;
+        }
+
+        if (!Objects.equals(first.getSequenceNumber(), second.getSequenceNumber())) {
+            return true;
+        }
+
+        return false;
     }
 
     /**
@@ -5102,6 +5137,10 @@
                     savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
             publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
                     savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
+            String appName = savedBundle.getString(EXTRA_SUBSTITUTE_APP_NAME);
+            if (appName != null) {
+                publicExtras.putString(EXTRA_SUBSTITUTE_APP_NAME, appName);
+            }
             mN.extras = publicExtras;
             RemoteViews view;
             if (ambient) {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 46566e7..21d6762 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -495,7 +495,15 @@
      * @hide
      */
     public boolean tasksAreFloating() {
-        return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED;
+        return isFloating(mWindowingMode);
+    }
+
+    /**
+     * Returns true if the windowingMode represents a floating window.
+     * @hide
+     */
+    public static boolean isFloating(int windowingMode) {
+        return windowingMode == WINDOWING_MODE_FREEFORM || windowingMode == WINDOWING_MODE_PINNED;
     }
 
     /**
diff --git a/core/java/android/app/slice/SliceMetrics.java b/core/java/android/app/slice/SliceMetrics.java
index 20c1390..746beaf 100644
--- a/core/java/android/app/slice/SliceMetrics.java
+++ b/core/java/android/app/slice/SliceMetrics.java
@@ -18,9 +18,11 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.metrics.LogMaker;
 import android.net.Uri;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 /**
  * Metrics interface for slices.
@@ -34,24 +36,38 @@
 
     private static final String TAG = "SliceMetrics";
     private MetricsLogger mMetricsLogger;
+    private LogMaker mLogMaker;
 
     /**
      * An object to be used throughout the life of a slice to register events.
      */
     public SliceMetrics(@NonNull Context context, @NonNull Uri uri) {
         mMetricsLogger = new MetricsLogger();
+        mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
+        mLogMaker.addTaggedData(MetricsEvent.FIELD_SLICE_AUTHORITY, uri.getAuthority());
+        mLogMaker.addTaggedData(MetricsEvent.FIELD_SLICE_PATH, uri.getPath());
     }
 
     /**
      * To be called whenever the slice becomes visible to the user.
      */
     public void logVisible() {
+        synchronized (mLogMaker)  {
+            mLogMaker.setCategory(MetricsEvent.SLICE)
+                    .setType(MetricsEvent.TYPE_OPEN);
+            mMetricsLogger.write(mLogMaker);
+        }
     }
 
     /**
      * To be called whenever the slice becomes invisible to the user.
      */
     public void logHidden() {
+        synchronized (mLogMaker)  {
+            mLogMaker.setCategory(MetricsEvent.SLICE)
+                    .setType(MetricsEvent.TYPE_CLOSE);
+            mMetricsLogger.write(mLogMaker);
+        }
     }
 
     /**
@@ -68,5 +84,12 @@
      * @param subSlice The URI of the sub-slice that is the subject of the interaction.
      */
     public void logTouch(int actionType, @NonNull Uri subSlice) {
+        synchronized (mLogMaker)  {
+            mLogMaker.setCategory(MetricsEvent.SLICE)
+                    .setType(MetricsEvent.TYPE_ACTION)
+                    .addTaggedData(MetricsEvent.FIELD_SUBSLICE_AUTHORITY, subSlice.getAuthority())
+                    .addTaggedData(MetricsEvent.FIELD_SUBSLICE_PATH, subSlice.getPath());
+            mMetricsLogger.write(mLogMaker);
+        }
     }
 }
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
new file mode 100644
index 0000000..aaae57e5
--- /dev/null
+++ b/core/java/android/app/usage/EventList.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import java.util.ArrayList;
+
+/**
+ * A container to keep {@link UsageEvents.Event usage events} in non-descending order of their
+ * {@link UsageEvents.Event#mTimeStamp timestamps}.
+ *
+ * @hide
+ */
+public class EventList {
+
+    private final ArrayList<UsageEvents.Event> mEvents;
+
+    /**
+     * Create a new event list with default capacity
+     */
+    public EventList() {
+        mEvents = new ArrayList<>();
+    }
+
+    /**
+     * Returns the size of the list
+     * @return the number of events in the list
+     */
+    public int size() {
+        return mEvents.size();
+    }
+
+    /**
+     * Removes all events from the list
+     */
+    public void clear() {
+        mEvents.clear();
+    }
+
+    /**
+     * Returns the {@link UsageEvents.Event event} at the specified position in this list.
+     * @param index the index of the event to return, such that {@code 0 <= index < size()}
+     * @return The {@link UsageEvents.Event event} at position {@code index}
+     */
+    public UsageEvents.Event get(int index) {
+        return mEvents.get(index);
+    }
+
+    /**
+     * Inserts the given {@link UsageEvents.Event event} into the list while keeping the list sorted
+     * based on the event {@link UsageEvents.Event#mTimeStamp timestamps}.
+     *
+     * @param event The event to insert
+     */
+    public void insert(UsageEvents.Event event) {
+        final int size = mEvents.size();
+        // fast case: just append if this is the latest event
+        if (size == 0 || event.mTimeStamp >= mEvents.get(size - 1).mTimeStamp) {
+            mEvents.add(event);
+            return;
+        }
+        // To minimize number of elements being shifted, insert at the first occurrence of the next
+        // greatest timestamp in the list.
+        final int insertIndex = firstIndexOnOrAfter(event.mTimeStamp + 1);
+        mEvents.add(insertIndex, event);
+    }
+
+    /**
+     * Finds the index of the first event whose timestamp is greater than or equal to the given
+     * timestamp.
+     *
+     * @param timeStamp The timestamp for which to search the list.
+     * @return The smallest {@code index} for which {@code (get(index).mTimeStamp >= timeStamp)} is
+     * {@code true}, or {@link #size() size} if no such {@code index} exists.
+     */
+    public int firstIndexOnOrAfter(long timeStamp) {
+        final int size = mEvents.size();
+        int result = size;
+        int lo = 0;
+        int hi = size - 1;
+        while (lo <= hi) {
+            final int mid = (lo + hi) >>> 1;
+            final long midTimeStamp = mEvents.get(mid).mTimeStamp;
+            if (midTimeStamp >= timeStamp) {
+                hi = mid - 1;
+                result = mid;
+            } else {
+                lo = mid + 1;
+            }
+        }
+        return result;
+    }
+}
diff --git a/core/java/android/app/usage/TimeSparseArray.java b/core/java/android/app/usage/TimeSparseArray.java
index 4ec0e9e..2bd6b24 100644
--- a/core/java/android/app/usage/TimeSparseArray.java
+++ b/core/java/android/app/usage/TimeSparseArray.java
@@ -27,14 +27,12 @@
 public class TimeSparseArray<E> extends LongSparseArray<E> {
     private static final String TAG = TimeSparseArray.class.getSimpleName();
 
+    private boolean mWtfReported;
+
     public TimeSparseArray() {
         super();
     }
 
-    public TimeSparseArray(int initialCapacity) {
-        super(initialCapacity);
-    }
-
     /**
      * Finds the index of the first element whose timestamp is greater or equal to
      * the given time.
@@ -75,22 +73,16 @@
     /**
      * {@inheritDoc}
      *
-     * Overridden to ensure no collisions. The key (time in milliseconds) is incremented till an
-     * empty place is found.
+     * <p> As this container is being used only to keep {@link android.util.AtomicFile files},
+     * there should not be any collisions. Reporting a {@link Slog#wtf(String, String)} in case that
+     * happens, as that will lead to one whole file being dropped.
      */
     @Override
     public void put(long key, E value) {
-        final long origKey = key;
-        int keyIndex = indexOfKey(key);
-        if (keyIndex >= 0) {
-            final long sz = size();
-            while (keyIndex < sz && keyAt(keyIndex) == key) {
-                key++;
-                keyIndex++;
-            }
-            if (key >= origKey + 100) {
-                Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
-                        + " displaced to " + key);
+        if (indexOfKey(key) >= 0) {
+            if (!mWtfReported) {
+                Slog.wtf(TAG, "Overwriting value " + get(key) + " by " + value);
+                mWtfReported = true;
             }
         }
         super.put(key, value);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1b6b5a0..1b9e27c 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -394,6 +394,58 @@
     public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
 
     /**
+     * Device only has a display.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_OUT = 0;
+
+    /**
+     * Device has a display and the ability to input Yes/No.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_IO = 1;
+
+    /**
+     * Device only has a keyboard for entry but no display.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_IN = 2;
+
+    /**
+     * Device has no Input or Output capability.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_NONE = 3;
+
+    /**
+     * Device has a display and a full keyboard.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_KBDISP = 4;
+
+    /**
+     * Maximum range value for Input/Output capabilities.
+     *
+     * <p>This should be updated when adding a new Input/Output capability. Other code
+     * like validation depends on this being accurate.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_MAX = 5;
+
+    /**
+     * The Input/Output capability of the device is unknown.
+     *
+     * @hide
+     */
+    public static final int IO_CAPABILITY_UNKNOWN = 255;
+
+    /**
      * Broadcast Action: The local Bluetooth adapter has started the remote
      * device discovery process.
      * <p>This usually involves an inquiry scan of about 12 seconds, followed
@@ -1225,6 +1277,106 @@
     }
 
     /**
+     * Returns the Input/Output capability of the device for classic Bluetooth.
+     *
+     * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+     *         {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
+     *         {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public int getIoCapability() {
+        if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getIoCapability();
+        } catch (RemoteException e) {
+            Log.e(TAG, e.getMessage(), e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+    }
+
+    /**
+     * Sets the Input/Output capability of the device for classic Bluetooth.
+     *
+     * <p>Changing the Input/Output capability of a device only takes effect on restarting the
+     * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
+     * and {@link BluetoothAdapter#enable()} to see the changes.
+     *
+     * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+     *                   {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
+     *                   {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setIoCapability(int capability) {
+        if (getState() != STATE_ON) return false;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.setIoCapability(capability);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.getMessage(), e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the Input/Output capability of the device for BLE operations.
+     *
+     * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+     *         {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE},
+     *         {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public int getLeIoCapability() {
+        if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getLeIoCapability();
+        } catch (RemoteException e) {
+            Log.e(TAG, e.getMessage(), e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
+    }
+
+    /**
+     * Sets the Input/Output capability of the device for BLE operations.
+     *
+     * <p>Changing the Input/Output capability of a device only takes effect on restarting the
+     * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()}
+     * and {@link BluetoothAdapter#enable()} to see the changes.
+     *
+     * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT},
+     *                   {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN},
+     *                   {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setLeIoCapability(int capability) {
+        if (getState() != STATE_ON) return false;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.setLeIoCapability(capability);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.getMessage(), e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return false;
+    }
+
+    /**
      * Get the current Bluetooth scan mode of the local Bluetooth adapter.
      * <p>The Bluetooth scan mode determines if the local adapter is
      * connectable and/or discoverable from remote Bluetooth devices.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index ac21395..7a6b72e 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -848,7 +848,11 @@
             return null;
         }
         try {
-            return service.getRemoteName(this);
+            String name = service.getRemoteName(this);
+            if (name != null) {
+                return name.replaceAll("[\\t\\n\\r]+", " ");
+            }
+            return null;
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ede7ee4..64998a3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -729,6 +729,7 @@
      * cases where system components are loaded into other app processes, in which
      * case this will be the name of the primary package in that process (so that app
      * ops uid verification will work with the name). */
+    @TestApi
     public abstract String getOpPackageName();
 
     /** Return the full application info for this context's package. */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 206ed71..dec2cd4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3659,6 +3659,10 @@
      * <p class="note">This is a protected intent that can only be sent by the system.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable} and the helper
+     * functions {@code ServiceStateTable.getUriForSubscriptionIdAndField} and
+     * {@code ServiceStateTable.getUriForSubscriptionId} to subscribe to changes to the ServiceState
+     * for a given subscription id and field with a ContentObserver or using JobScheduler.
      */
     @Deprecated
     @SystemApi
@@ -3674,6 +3678,7 @@
      * @see android.telephony.ServiceState#STATE_POWER_OFF
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_REG_STATE}.
      */
     @Deprecated
     @SystemApi
@@ -3687,6 +3692,7 @@
      * @see android.telephony.ServiceState#STATE_POWER_OFF
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_REG_STATE}.
      */
     @Deprecated
     @SystemApi
@@ -3697,6 +3703,7 @@
      * type.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_ROAMING_TYPE}.
      */
     @Deprecated
     @SystemApi
@@ -3707,6 +3714,7 @@
      * type.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_ROAMING_TYPE}.
      */
     @Deprecated
     @SystemApi
@@ -3718,6 +3726,8 @@
      * {@code null} if the operator name is not known or unregistered.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_ALPHA_LONG}.
      */
     @Deprecated
     @SystemApi
@@ -3729,6 +3739,8 @@
      * {@code null} if the operator name is not known or unregistered.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_ALPHA_SHORT}.
      */
     @Deprecated
     @SystemApi
@@ -3740,6 +3752,7 @@
      * network.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#VOICE_OPERATOR_NUMERIC}.
      */
     @Deprecated
     @SystemApi
@@ -3751,6 +3764,8 @@
      * {@code null} if the operator name is not known or unregistered.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_ALPHA_LONG}.
      */
     @Deprecated
     @SystemApi
@@ -3762,6 +3777,8 @@
      * {@code null} if the operator name is not known or unregistered.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_ALPHA_SHORT}.
      */
     @Deprecated
     @SystemApi
@@ -3773,6 +3790,7 @@
      * data operator.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#DATA_OPERATOR_NUMERIC}.
      */
     @Deprecated
     @SystemApi
@@ -3784,6 +3802,8 @@
      * Will be {@code true} if manual mode, {@code false} if automatic mode.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#IS_MANUAL_NETWORK_SELECTION}.
      */
     @Deprecated
     @SystemApi
@@ -3794,6 +3814,8 @@
      * radio technology.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#RIL_VOICE_RADIO_TECHNOLOGY}.
      */
     @Deprecated
     @SystemApi
@@ -3804,6 +3826,8 @@
      * radio technology.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#RIL_DATA_RADIO_TECHNOLOGY}.
      */
     @Deprecated
     @SystemApi
@@ -3815,6 +3839,7 @@
      * Will be {@code true} if support, {@code false} otherwise.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#CSS_INDICATOR}.
      */
     @Deprecated
     @SystemApi
@@ -3825,6 +3850,7 @@
      * id. {@code Integer.MAX_VALUE} if unknown.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#NETWORK_ID}.
      */
     @Deprecated
     @SystemApi
@@ -3835,6 +3861,7 @@
      * {@code Integer.MAX_VALUE} if unknown.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#SYSTEM_ID}.
      */
     @Deprecated
     @SystemApi
@@ -3845,6 +3872,7 @@
      * indicator if registered on a CDMA or EVDO system or {@code -1} if not.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#CDMA_ROAMING_INDICATOR}.
      */
     @Deprecated
     @SystemApi
@@ -3855,6 +3883,8 @@
      * indicator from the PRL if registered on a CDMA or EVDO system {@code -1} if not.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#CDMA_DEFAULT_ROAMING_INDICATOR}.
      */
     @Deprecated
     @SystemApi
@@ -3866,6 +3896,7 @@
      * {@code true} if in emergency only mode, {@code false} otherwise.
      * @hide
      * @removed
+     * @deprecated Use {@link android.provider.Telephony.ServiceStateTable#IS_EMERGENCY_ONLY}.
      */
     @Deprecated
     @SystemApi
@@ -3877,6 +3908,8 @@
      * {@code true} if registration indicates roaming, {@code false} otherwise
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#IS_DATA_ROAMING_FROM_REGISTRATION}.
      */
     @Deprecated
     @SystemApi
@@ -3889,6 +3922,8 @@
      * {@code true} if carrier aggregation is in use, {@code false} otherwise.
      * @hide
      * @removed
+     * @deprecated Use
+     * {@link android.provider.Telephony.ServiceStateTable#IS_USING_CARRIER_AGGREGATION}.
      */
     @Deprecated
     @SystemApi
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index a05a8ec..83a0228 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -529,14 +529,32 @@
     private boolean swapBuffers(EGLSurface surface)
             throws LegacyExceptionUtils.BufferQueueAbandonedException {
         boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
+
         int error = EGL14.eglGetError();
-        if (error == EGL14.EGL_BAD_SURFACE) {
-            throw new LegacyExceptionUtils.BufferQueueAbandonedException();
-        } else if (error != EGL14.EGL_SUCCESS) {
-            throw new IllegalStateException("swapBuffers: EGL error: 0x" +
-                    Integer.toHexString(error));
+        switch (error) {
+            case EGL14.EGL_SUCCESS:
+                return result;
+
+            // Check for an abandoned buffer queue, or other error conditions out
+            // of the user's control.
+            //
+            // From the EGL 1.4 spec (2013-12-04), Section 3.9.4 Posting Errors:
+            //
+            //   If eglSwapBuffers is called and the native window associated with
+            //   surface is no longer valid, an EGL_BAD_NATIVE_WINDOW error is
+            //   generated.
+            //
+            // We also interpret EGL_BAD_SURFACE as indicating an abandoned
+            // surface, even though the EGL spec does not document it as such, for
+            // backwards compatibility with older versions of this file.
+            case EGL14.EGL_BAD_NATIVE_WINDOW:
+            case EGL14.EGL_BAD_SURFACE:
+                throw new LegacyExceptionUtils.BufferQueueAbandonedException();
+
+            default:
+                throw new IllegalStateException(
+                        "swapBuffers: EGL error: 0x" + Integer.toHexString(error));
         }
-        return result;
     }
 
     private void checkEglError(String msg) {
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index 6e51060..baa7a50 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -269,6 +269,29 @@
         mBundle = in.readBundle();
     }
 
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("RadioMetadata[");
+
+        final String removePrefix = "android.hardware.radio.metadata";
+
+        boolean first = true;
+        for (String key : mBundle.keySet()) {
+            if (first) first = false;
+            else sb.append(", ");
+
+            String keyDisp = key;
+            if (key.startsWith(removePrefix)) keyDisp = key.substring(removePrefix.length());
+
+            sb.append(keyDisp);
+            sb.append('=');
+            sb.append(mBundle.get(key));
+        }
+
+        sb.append("]");
+        return sb.toString();
+    }
+
     /**
      * Returns {@code true} if the given key is contained in the meta data
      *
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index f5a7433..0fef78d 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -40,8 +40,9 @@
     private static final String TAG = "Bundle";
     static final boolean DEBUG = false;
 
-    // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
-    static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+    // Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
+    private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+    private static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'
 
     /**
      * Flag indicating that this Bundle is okay to "defuse." That is, it's okay
@@ -91,6 +92,11 @@
     Parcel mParcelledData = null;
 
     /**
+     * Whether {@link #mParcelledData} was generated by native coed or not.
+     */
+    private boolean mParcelledByNative;
+
+    /**
      * The ClassLoader used when unparcelling data from mParcelledData.
      */
     private ClassLoader mClassLoader;
@@ -223,7 +229,7 @@
         synchronized (this) {
             final Parcel source = mParcelledData;
             if (source != null) {
-                initializeFromParcelLocked(source, /*recycleParcel=*/ true);
+                initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "unparcel "
@@ -234,7 +240,8 @@
         }
     }
 
-    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {
+    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
+            boolean parcelledByNative) {
         if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
             Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
                     + "clobber all data inside!", new Throwable());
@@ -251,6 +258,7 @@
                 mMap.erase();
             }
             mParcelledData = null;
+            mParcelledByNative = false;
             return;
         }
 
@@ -270,7 +278,15 @@
             map.ensureCapacity(count);
         }
         try {
-            parcelledData.readArrayMapInternal(map, count, mClassLoader);
+            if (parcelledByNative) {
+                // If it was parcelled by native code, then the array map keys aren't sorted
+                // by their hash codes, so use the safe (slow) one.
+                parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
+            } else {
+                // If parcelled by Java, we know the contents are sorted properly,
+                // so we can use ArrayMap.append().
+                parcelledData.readArrayMapInternal(map, count, mClassLoader);
+            }
         } catch (BadParcelableException e) {
             if (sShouldDefuse) {
                 Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -284,6 +300,7 @@
                 recycleParcel(parcelledData);
             }
             mParcelledData = null;
+            mParcelledByNative = false;
         }
         if (DEBUG) {
             Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
@@ -403,14 +420,17 @@
             if (from.mParcelledData != null) {
                 if (from.isEmptyParcel()) {
                     mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+                    mParcelledByNative = false;
                 } else {
                     mParcelledData = Parcel.obtain();
                     mParcelledData.appendFrom(from.mParcelledData, 0,
                             from.mParcelledData.dataSize());
                     mParcelledData.setDataPosition(0);
+                    mParcelledByNative = from.mParcelledByNative;
                 }
             } else {
                 mParcelledData = null;
+                mParcelledByNative = false;
             }
 
             if (from.mMap != null) {
@@ -1538,7 +1558,7 @@
                 } else {
                     int length = mParcelledData.dataSize();
                     parcel.writeInt(length);
-                    parcel.writeInt(BUNDLE_MAGIC);
+                    parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
                     parcel.appendFrom(mParcelledData, 0, length);
                 }
                 return;
@@ -1585,11 +1605,14 @@
         } else if (length == 0) {
             // Empty Bundle or end of data.
             mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+            mParcelledByNative = false;
             return;
         }
 
         final int magic = parcel.readInt();
-        if (magic != BUNDLE_MAGIC) {
+        final boolean isJavaBundle = magic == BUNDLE_MAGIC;
+        final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
+        if (!isJavaBundle && !isNativeBundle) {
             throw new IllegalStateException("Bad magic number for Bundle: 0x"
                     + Integer.toHexString(magic));
         }
@@ -1598,7 +1621,7 @@
             // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
             // unparcel right away.
             synchronized (this) {
-                initializeFromParcelLocked(parcel, /*recycleParcel=*/ false);
+                initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
             }
             return;
         }
@@ -1616,6 +1639,7 @@
         p.setDataPosition(0);
 
         mParcelledData = p;
+        mParcelledByNative = isNativeBundle;
     }
 
     /** {@hide} */
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 848c596..f17e0f0 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -77,10 +77,18 @@
     private Printer mLogging;
     private long mTraceTag;
 
-    /* If set, the looper will show a warning log if a message dispatch takes longer than time. */
+    /**
+     * If set, the looper will show a warning log if a message dispatch takes longer than this.
+     */
     private long mSlowDispatchThresholdMs;
 
-     /** Initialize the current thread as a looper.
+    /**
+     * If set, the looper will show a warning log if a message delivery (actual delivery time -
+     * post time) takes longer than this.
+     */
+    private long mSlowDeliveryThresholdMs;
+
+    /** Initialize the current thread as a looper.
       * This gives you a chance to create handlers that then reference
       * this looper, before actually starting the loop. Be sure to call
       * {@link #loop()} after calling this method, and end it by calling
@@ -138,6 +146,16 @@
         Binder.clearCallingIdentity();
         final long ident = Binder.clearCallingIdentity();
 
+        // Allow overriding a threshold with a system prop. e.g.
+        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
+        final int thresholdOverride =
+                SystemProperties.getInt("log.looper."
+                        + Process.myUid() + "."
+                        + Thread.currentThread().getName()
+                        + ".slow", 0);
+
+        boolean slowDeliveryDetected = false;
+
         for (;;) {
             Message msg = queue.next(); // might block
             if (msg == null) {
@@ -152,30 +170,50 @@
                         msg.callback + ": " + msg.what);
             }
 
-            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
-
             final long traceTag = me.mTraceTag;
+            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
+            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
+            if (thresholdOverride > 0) {
+                slowDispatchThresholdMs = thresholdOverride;
+                slowDeliveryThresholdMs = thresholdOverride;
+            }
+            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
+            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
+
+            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
+            final boolean needEndTime = logSlowDispatch;
+
             if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                 Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
             }
-            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
-            final long end;
+
+            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
+            final long dispatchEnd;
             try {
                 msg.target.dispatchMessage(msg);
-                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
+                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
             } finally {
                 if (traceTag != 0) {
                     Trace.traceEnd(traceTag);
                 }
             }
-            if (slowDispatchThresholdMs > 0) {
-                final long time = end - start;
-                if (time > slowDispatchThresholdMs) {
-                    Slog.w(TAG, "Dispatch took " + time + "ms on "
-                            + Thread.currentThread().getName() + ", h=" +
-                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
+            if (logSlowDelivery) {
+                if (slowDeliveryDetected) {
+                    if ((dispatchStart - msg.when) <= 10) {
+                        Slog.w(TAG, "Drained");
+                        slowDeliveryDetected = false;
+                    }
+                } else {
+                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
+                            msg)) {
+                        // Once we write a slow delivery log, suppress until the queue drains.
+                        slowDeliveryDetected = true;
+                    }
                 }
             }
+            if (logSlowDispatch) {
+                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
+            }
 
             if (logging != null) {
                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
@@ -196,6 +234,19 @@
         }
     }
 
+    private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
+            String what, Message msg) {
+        final long actualTime = measureEnd - measureStart;
+        if (actualTime < threshold) {
+            return false;
+        }
+        // For slow delivery, the current message isn't really important, but log it anyway.
+        Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+                + Thread.currentThread().getName() + " h="
+                + msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
+        return true;
+    }
+
     /**
      * Return the Looper object associated with the current thread.  Returns
      * null if the calling thread is not associated with a Looper.
@@ -243,9 +294,13 @@
         mTraceTag = traceTag;
     }
 
-    /** {@hide} */
-    public void setSlowDispatchThresholdMs(long slowDispatchThresholdMs) {
+    /**
+     * Set a thresholds for slow dispatch/delivery log.
+     * {@hide}
+     */
+    public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
         mSlowDispatchThresholdMs = slowDispatchThresholdMs;
+        mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
     }
 
     /**
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 5142928..6d76c0b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -715,7 +715,13 @@
         nativeWriteString(mNativePtr, val);
     }
 
-    /** @hide */
+    /**
+     * Write a boolean value into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     *
+     * <p>Note: This method currently delegates to writeInt with a value of 1 or 0
+     * for true or false, respectively, but may change in the future.
+     */
     public final void writeBoolean(boolean val) {
         writeInt(val ? 1 : 0);
     }
@@ -805,6 +811,9 @@
     /**
      * Write a byte value into the parcel at the current dataPosition(),
      * growing dataCapacity() if needed.
+     *
+     * <p>Note: This method currently delegates to writeInt but may change in
+     * the future.
      */
     public final void writeByte(byte val) {
         writeInt(val);
@@ -2007,7 +2016,9 @@
         return nativeReadString(mNativePtr);
     }
 
-    /** @hide */
+    /**
+     * Read a boolean value from the parcel at the current dataPosition().
+     */
     public final boolean readBoolean() {
         return readInt() != 0;
     }
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index fa05a5e..5228d6d 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -247,7 +247,11 @@
         }
         if (DEBUG) Slog.d(TAG, "Error: Unable to open file: " + path);
         getErrPrintWriter().println("Error: Unable to open file: " + path);
-        getErrPrintWriter().println("Consider using a file under /data/local/tmp/");
+
+        String suggestedPath = "/data/local/tmp/";
+        if (path == null || !path.startsWith(suggestedPath)) {
+            getErrPrintWriter().println("Consider using a file under " + suggestedPath);
+        }
         return null;
     }
 
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 30451c2..583f060 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -93,6 +93,8 @@
     public static final long TRACE_TAG_VIBRATOR = 1L << 23;
     /** @hide */
     public static final long TRACE_TAG_AIDL = 1L << 24;
+    /** @hide */
+    public static final long TRACE_TAG_NNAPI = 1L << 25;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 4d4f31d..66ebbdb 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -145,6 +145,7 @@
      * "it's system", because of isolated UIDs. Use {@link #isCore} for that.
      * @hide
      */
+    @TestApi
     public static boolean isApp(int uid) {
         if (uid > 0) {
             final int appId = getAppId(uid);
diff --git a/core/java/android/printservice/recommendation/RecommendationService.java b/core/java/android/printservice/recommendation/RecommendationService.java
index 733629a..968a625 100644
--- a/core/java/android/printservice/recommendation/RecommendationService.java
+++ b/core/java/android/printservice/recommendation/RecommendationService.java
@@ -119,14 +119,16 @@
                     mCallbacks = null;
                     break;
                 case MSG_UPDATE:
-                    // Note that there might be a connection change in progress. In this case the
-                    // message is handled as before the change. This is acceptable as the caller of
-                    // the connection change has not guarantee when the connection change binder
-                    // transaction is actually processed.
-                    try {
-                        mCallbacks.onRecommendationsUpdated((List<RecommendationInfo>) msg.obj);
-                    } catch (RemoteException | NullPointerException e) {
-                        Log.e(LOG_TAG, "Could not update recommended services", e);
+                    if (mCallbacks != null) {
+                        // Note that there might be a connection change in progress. In this case
+                        // the message is handled as before the change. This is acceptable as the
+                        // caller of the connection change has not guarantee when the connection
+                        // change binder transaction is actually processed.
+                        try {
+                            mCallbacks.onRecommendationsUpdated((List<RecommendationInfo>) msg.obj);
+                        } catch (RemoteException | NullPointerException e) {
+                            Log.e(LOG_TAG, "Could not update recommended services", e);
+                        }
                     }
                     break;
             }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index c94da9a..f7409d0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -5050,6 +5051,7 @@
         *
         * @hide
         */
+        @TestApi
         public static final Uri CORP_CONTENT_URI =
                 Uri.withAppendedPath(AUTHORITY_URI, "raw_contact_entities_corp");
 
@@ -6057,6 +6059,7 @@
             *
             * @hide
             */
+            @TestApi
             public static final Uri ENTERPRISE_CONTENT_URI =
                     Uri.withAppendedPath(Data.ENTERPRISE_CONTENT_URI, "phones");
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index da45269..6c57d88 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -86,6 +86,7 @@
 import android.view.textservice.TextServicesManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.widget.ILockSettings;
 
 import java.io.IOException;
@@ -3147,7 +3148,9 @@
         public static final String DISPLAY_COLOR_MODE = "display_color_mode";
 
         private static final Validator DISPLAY_COLOR_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new SettingsValidators.InclusiveIntegerRangeValidator(
+                        ColorDisplayController.COLOR_MODE_NATURAL,
+                        ColorDisplayController.COLOR_MODE_AUTOMATIC);
 
         /**
          * The amount of time in milliseconds before the device goes to sleep or begins
@@ -10784,6 +10787,7 @@
          * read_binary_cpu_time          (boolean)
          * proc_state_cpu_times_read_delay_ms (long)
          * external_stats_collection_rate_limit_ms (long)
+         * battery_level_collection_delay_ms (long)
          * </pre>
          *
          * <p>
diff --git a/core/java/android/security/net/config/WfaCertificateSource.java b/core/java/android/security/net/config/WfaCertificateSource.java
new file mode 100644
index 0000000..f212ef8
--- /dev/null
+++ b/core/java/android/security/net/config/WfaCertificateSource.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.io.File;
+
+/**
+ * {@link CertificateSource} based on the system WFA CA store.
+ * @hide
+ */
+public final class WfaCertificateSource extends DirectoryCertificateSource {
+    private static class NoPreloadHolder {
+        private static final WfaCertificateSource INSTANCE = new WfaCertificateSource();
+    }
+
+    private WfaCertificateSource() {
+        super(new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts_wfa"));
+    }
+
+    public static WfaCertificateSource getInstance() {
+        return NoPreloadHolder.INSTANCE;
+    }
+
+    @Override
+    protected boolean isCertMarkedAsRemoved(String caFile) {
+        return false;
+    }
+}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index 02be403..311a8d2 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -189,6 +189,8 @@
             source = SystemCertificateSource.getInstance();
         } else if ("user".equals(sourceString)) {
             source = UserCertificateSource.getInstance();
+        } else if ("wfa".equals(sourceString)) {
+            source = WfaCertificateSource.getInstance();
         } else {
             throw new ParserException(parser, "Unknown certificates src. "
                     + "Should be one of system|user|@resourceVal");
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index 90acc88..2ba0376 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -82,6 +82,9 @@
          * {@link #transformChild(int, Transformation) transformations} are applied to the children
          * views.
          *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         *
          * @param updates a {@link RemoteViews} with the updated actions to be applied in the
          * underlying presentation template.
          *
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index ccec483..5211767 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -336,6 +336,9 @@
          * higher, datasets that require authentication can be also be filtered by passing a
          * {@link AutofillValue#forText(CharSequence) text value} as the  {@code value} parameter.
          *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         *
          * @param id id returned by {@link
          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 2bc4b8f..7bf1f83 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -241,6 +241,9 @@
          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
          * platform needs to fill in the authentication arguments.
          *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         *
          * @param authentication Intent to an activity with your authentication flow.
          * @param presentation The presentation to visualize the response.
          * @param ids id of Views that when focused will display the authentication UI.
@@ -449,6 +452,9 @@
          * authentication (as the header could have been set directly in the main presentation in
          * these cases).
          *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         *
          * @param header a presentation to represent the header. This presentation is not clickable
          * &mdash;calling
          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
@@ -477,6 +483,9 @@
          * authentication (as the footer could have been set directly in the main presentation in
          * these cases).
          *
+         * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+         * or background color: Autofill on different platforms may have different themes.
+         *
          * @param footer a presentation to represent the footer. This presentation is not clickable
          * &mdash;calling
          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7e54647..db34856 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -700,15 +700,9 @@
                     mIsCreating = false;
                     if (mSurfaceControl != null && !mSurfaceCreated) {
                         mSurface.release();
-                        // If we are not in the stopped state, then the destruction of the Surface
-                        // represents a visual change we need to display, and we should go ahead
-                        // and destroy the SurfaceControl. However if we are in the stopped state,
-                        // we can just leave the Surface around so it can be a part of animations,
-                        // and we let the life-time be tied to the parent surface.
-                        if (!mWindowStopped) {
-                            mSurfaceControl.destroy();
-                            mSurfaceControl = null;
-                        }
+
+                        mSurfaceControl.destroy();
+                        mSurfaceControl = null;
                     }
                 }
             } catch (Exception ex) {
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index a6495d1..8266207 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -19,6 +19,7 @@
 import static android.view.autofill.Helper.sVerbose;
 
 import android.annotation.NonNull;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
@@ -79,11 +80,6 @@
     public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
         mWindowPresenter = new WindowPresenter(presenter);
 
-        // We want to show the window as system controlled one so it covers app windows, but it has
-        // to be an application type (so it's contained inside the application area).
-        // Hence, we set it to the application type with the highest z-order, which currently
-        // is TYPE_APPLICATION_ABOVE_SUB_PANEL.
-        setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
         setTouchModal(false);
         setOutsideTouchable(true);
         setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
@@ -110,7 +106,16 @@
      */
     public void update(View anchor, int offsetX, int offsetY, int width, int height,
             Rect virtualBounds) {
-        mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
+        mFullScreen = width == LayoutParams.MATCH_PARENT;
+        // For no fullscreen autofill window, we want to show the window as system controlled one
+        // so it covers app windows, but it has to be an application type (so it's contained inside
+        // the application area). Hence, we set it to the application type with the highest z-order,
+        // which currently is TYPE_APPLICATION_ABOVE_SUB_PANEL.
+        // For fullscreen mode, autofill window is at the bottom of screen, it should not be
+        // clipped by app activity window. Fullscreen autofill window does not need to follow app
+        // anchor view position.
+        setWindowLayoutType(mFullScreen ? WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
+                : WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
         // If we are showing the popup for a virtual view we use a fake view which
         // delegates to the anchor but present itself with the same bounds as the
         // virtual view. This ensures that the location logic in popup works
@@ -119,6 +124,15 @@
         if (mFullScreen) {
             offsetX = 0;
             offsetY = 0;
+            // If it is not fullscreen height, put window at bottom. Computes absolute position.
+            // Note that we cannot easily change default gravity from Gravity.TOP to
+            // Gravity.BOTTOM because PopupWindow base class does not expose computeGravity().
+            final Point outPoint = new Point();
+            anchor.getContext().getDisplay().getSize(outPoint);
+            width = outPoint.x;
+            if (height != LayoutParams.MATCH_PARENT) {
+                offsetY = outPoint.y - height;
+            }
             actualAnchor = anchor;
         } else if (virtualBounds != null) {
             final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
@@ -202,6 +216,16 @@
             actualAnchor = anchor;
         }
 
+        if (!mFullScreen) {
+            // No fullscreen window animation is controlled by PopupWindow.
+            setAnimationStyle(-1);
+        } else if (height == LayoutParams.MATCH_PARENT) {
+            // Complete fullscreen autofill window has no animation.
+            setAnimationStyle(0);
+        } else {
+            // Slide half screen height autofill window from bottom.
+            setAnimationStyle(com.android.internal.R.style.AutofillHalfScreenAnimation);
+        }
         if (!isShowing()) {
             setWidth(width);
             setHeight(height);
@@ -223,7 +247,12 @@
     protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
             int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
         if (mFullScreen) {
-            // Do not patch LayoutParams if force full screen
+            // In fullscreen mode, don't need consider the anchor view.
+            outParams.x = xOffset;
+            outParams.y = yOffset;
+            outParams.width = width;
+            outParams.height = height;
+            outParams.gravity = gravity;
             return false;
         }
         return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
@@ -316,11 +345,6 @@
     }
 
     @Override
-    public void setAnimationStyle(int animationStyle) {
-        throw new IllegalStateException("You can't call this!");
-    }
-
-    @Override
     public void setBackgroundDrawable(Drawable background) {
         throw new IllegalStateException("You can't call this!");
     }
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 490c389..da86b55 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -55,7 +55,8 @@
         mManagerService = ITextClassifierService.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
         mSettings = Preconditions.checkNotNull(settings);
-        mFallback = new TextClassifierImpl(context, settings);
+        mFallback = context.getSystemService(TextClassificationManager.class)
+                .getTextClassifier(TextClassifier.LOCAL);
         mPackageName = Preconditions.checkNotNull(context.getPackageName());
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 262d9b8..1737861 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -191,10 +191,11 @@
         synchronized (mLock) {
             if (mLocalTextClassifier == null) {
                 if (mSettings.isLocalTextClassifierEnabled()) {
-                    mLocalTextClassifier = new TextClassifierImpl(mContext, mSettings);
+                    mLocalTextClassifier =
+                            new TextClassifierImpl(mContext, mSettings, TextClassifier.NO_OP);
                 } else {
                     Log.d(LOG_TAG, "Local TextClassifier disabled");
-                    mLocalTextClassifier = TextClassifierImpl.NO_OP;
+                    mLocalTextClassifier = TextClassifier.NO_OP;
                 }
             }
             return mLocalTextClassifier;
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 910fcaa..2cf265d 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
+import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.app.SearchManager;
 import android.content.ComponentName;
@@ -98,13 +99,18 @@
 
     private final TextClassificationConstants mSettings;
 
-    public TextClassifierImpl(Context context, TextClassificationConstants settings) {
+    public TextClassifierImpl(
+            Context context, TextClassificationConstants settings, TextClassifier fallback) {
         mContext = Preconditions.checkNotNull(context);
-        mFallback = TextClassifier.NO_OP;
+        mFallback = Preconditions.checkNotNull(fallback);
         mSettings = Preconditions.checkNotNull(settings);
         mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
     }
 
+    public TextClassifierImpl(Context context, TextClassificationConstants settings) {
+        this(context, settings, TextClassifier.NO_OP);
+    }
+
     /** @inheritDoc */
     @Override
     @WorkerThread
@@ -413,6 +419,9 @@
         for (LabeledIntent labeledIntent : IntentFactory.create(
                 mContext, referenceTime, highestScoringResult, classifiedText)) {
             final RemoteAction action = labeledIntent.asRemoteAction(mContext);
+            if (action == null) {
+                continue;
+            }
             if (isPrimaryAction) {
                 // For O backwards compatibility, the first RemoteAction is also written to the
                 // legacy API fields.
@@ -601,6 +610,7 @@
             return mRequestCode;
         }
 
+        @Nullable
         RemoteAction asRemoteAction(Context context) {
             final PackageManager pm = context.getPackageManager();
             final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
@@ -622,8 +632,12 @@
                 icon = Icon.createWithResource("android",
                         com.android.internal.R.drawable.ic_more_items);
             }
-            final RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
-                    TextClassification.createPendingIntent(context, mIntent, mRequestCode));
+            final PendingIntent pendingIntent =
+                    TextClassification.createPendingIntent(context, mIntent, mRequestCode);
+            if (pendingIntent == null) {
+                return null;
+            }
+            final RemoteAction action = new RemoteAction(icon, mTitle, mDescription, pendingIntent);
             action.setShouldShowIcon(shouldShowIcon);
             return action;
         }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f6ac1cc..10de449 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -123,6 +123,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.internal.view.FloatingActionMode;
 import com.android.internal.widget.EditableInputConnection;
 
 import java.lang.annotation.Retention;
@@ -2215,6 +2216,15 @@
         ActionMode.Callback actionModeCallback = new TextActionModeCallback(actionMode);
         mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
 
+        final boolean selectableText = mTextView.isTextEditable() || mTextView.isTextSelectable();
+        if (actionMode == TextActionMode.TEXT_LINK && !selectableText
+                && mTextActionMode instanceof FloatingActionMode) {
+            // Make the toolbar outside-touchable so that it can be dismissed when the user clicks
+            // outside of it.
+            ((FloatingActionMode) mTextActionMode).setOutsideTouchable(true,
+                    () -> stopTextActionMode());
+        }
+
         final boolean selectionStarted = mTextActionMode != null;
         if (selectionStarted
                 && mTextView.isTextEditable() && !mTextView.isTextSelectable()
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5ecbf90a..4865dab 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@
 import android.annotation.ColorInt;
 import android.annotation.DimenRes;
 import android.annotation.NonNull;
+import android.annotation.StyleRes;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application;
@@ -56,6 +57,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
 import android.view.RemotableViewMethod;
@@ -182,6 +184,12 @@
     private boolean mIsRoot = true;
 
     /**
+     * Optional theme resource id applied in inflateView(). When 0, Theme.DeviceDefault will be
+     * used.
+     */
+    private int mApplyThemeResId;
+
+    /**
      * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
      * the layout in a way that isn't recoverable, since views are being removed.
      */
@@ -3267,6 +3275,14 @@
     }
 
     /**
+     * Set the theme used in apply() and applyASync().
+     * @hide
+     */
+    public void setApplyTheme(@StyleRes int themeResId) {
+        mApplyThemeResId = themeResId;
+    }
+
+    /**
      * Inflates the view hierarchy represented by this object and applies
      * all of the actions.
      *
@@ -3301,6 +3317,10 @@
         final Context contextForResources = getContextForResources(context);
         Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
 
+        // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
+        if (mApplyThemeResId != 0) {
+            inflationContext = new ContextThemeWrapper(inflationContext, mApplyThemeResId);
+        }
         LayoutInflater inflater = (LayoutInflater)
                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index f1539ee..6cc964b 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -82,7 +82,7 @@
     public static final int AUTO_MODE_TWILIGHT = 2;
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED })
+    @IntDef({ COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED, COLOR_MODE_AUTOMATIC })
     public @interface ColorMode {}
 
     /**
@@ -103,12 +103,12 @@
      * @see #setColorMode(int)
      */
     public static final int COLOR_MODE_SATURATED = 2;
-
     /**
-     * See com.android.server.display.DisplayTransformManager.
+     * Color mode with automatic colors.
+     *
+     * @see #setColorMode(int)
      */
-    private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
-    private static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode";
+    public static final int COLOR_MODE_AUTOMATIC = 3;
 
     private final Context mContext;
     private final int mUserId;
@@ -362,6 +362,41 @@
     }
 
     /**
+     * Get the current color mode from system properties, or return -1.
+     *
+     * See com.android.server.display.DisplayTransformManager.
+     */
+    private @ColorMode int getCurrentColorModeFromSystemProperties() {
+        int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
+        if (displayColorSetting == 0) {
+            return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
+                ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
+        } else if (displayColorSetting == 1) {
+            return COLOR_MODE_SATURATED;
+        } else if (displayColorSetting == 2) {
+            return COLOR_MODE_AUTOMATIC;
+        } else {
+            return -1;
+        }
+    }
+
+    private boolean isColorModeAvailable(@ColorMode int colorMode) {
+        // SATURATED is always allowed
+        if (colorMode == COLOR_MODE_SATURATED) {
+            return true;
+        }
+
+        final int[] availableColorModes = mContext.getResources().getIntArray(
+                R.array.config_availableColorModes);
+        for (int mode : availableColorModes) {
+            if (mode == colorMode) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Get the current color mode.
      */
     public int getColorMode() {
@@ -369,17 +404,27 @@
             return COLOR_MODE_SATURATED;
         }
 
-        final int colorMode = System.getIntForUser(mContext.getContentResolver(),
+        int colorMode = System.getIntForUser(mContext.getContentResolver(),
             System.DISPLAY_COLOR_MODE, -1, mUserId);
-        if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) {
+        if (colorMode == -1) {
             // There still might be a legacy system property controlling color mode that we need to
             // respect.
-            if ("1".equals(SystemProperties.get(PERSISTENT_PROPERTY_NATIVE_MODE))) {
-                return COLOR_MODE_SATURATED;
-            }
-            return "1.0".equals(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
-                    ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
+            colorMode = getCurrentColorModeFromSystemProperties();
         }
+
+        // This happens when a color mode is no longer available (e.g., after system update or B&R)
+        // or the device does not support any color mode.
+        if (!isColorModeAvailable(colorMode)) {
+            if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
+                colorMode = COLOR_MODE_NATURAL;
+            } else if (colorMode == COLOR_MODE_SATURATED
+                && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
+                colorMode = COLOR_MODE_AUTOMATIC;
+            } else {
+                colorMode = COLOR_MODE_SATURATED;
+            }
+        }
+
         return colorMode;
     }
 
@@ -389,7 +434,7 @@
      * @param colorMode the color mode
      */
     public void setColorMode(@ColorMode int colorMode) {
-        if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) {
+        if (!isColorModeAvailable(colorMode)) {
             throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
         }
         System.putIntForUser(mContext.getContentResolver(), System.DISPLAY_COLOR_MODE, colorMode,
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index 7558f8c..eada142 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -18,12 +18,15 @@
 
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Trace;
 
 /**
  * Shared singleton background thread for each process.
  */
 public final class BackgroundThread extends HandlerThread {
+    private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
+    private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
     private static BackgroundThread sInstance;
     private static Handler sHandler;
 
@@ -35,7 +38,10 @@
         if (sInstance == null) {
             sInstance = new BackgroundThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+            final Looper looper = sInstance.getLooper();
+            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+            looper.setSlowLogThresholdMs(
+                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a4680ca..7703052 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -585,6 +585,7 @@
                 boolean onBatteryScreenOff);
         Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
         void cancelCpuSyncDueToWakelockChange();
+        Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
     }
 
     public Handler mHandler;
@@ -4083,7 +4084,8 @@
 
     boolean ensureStartClockTime(final long currentTime) {
         final long ABOUT_ONE_YEAR = 365*24*60*60*1000L;
-        if (currentTime > ABOUT_ONE_YEAR && mStartClockTime < (currentTime-ABOUT_ONE_YEAR)) {
+        if ((currentTime > ABOUT_ONE_YEAR && mStartClockTime < (currentTime-ABOUT_ONE_YEAR))
+                || (mStartClockTime > currentTime)) {
             // If the start clock time has changed by more than a year, then presumably
             // the previous time was completely bogus.  So we are going to figure out a
             // new time based on how much time has elapsed since we started counting.
@@ -12614,7 +12616,8 @@
 
                 // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
                 // which will pull external stats.
-                scheduleSyncExternalStatsLocked("battery-level", ExternalStatsSync.UPDATE_ALL);
+                mExternalSync.scheduleSyncDueToBatteryLevelChange(
+                        mConstants.BATTERY_LEVEL_COLLECTION_DELAY_MS);
             }
             if (mHistoryCur.batteryStatus != status) {
                 mHistoryCur.batteryStatus = (byte)status;
@@ -13270,6 +13273,8 @@
                 = "uid_remove_delay_ms";
         public static final String KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
                 = "external_stats_collection_rate_limit_ms";
+        public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
+                = "battery_level_collection_delay_ms";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
@@ -13277,6 +13282,7 @@
         private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
         private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
         private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
+        private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
@@ -13285,6 +13291,8 @@
         public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
         public long EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
                 = DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
+        public long BATTERY_LEVEL_COLLECTION_DELAY_MS
+                = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13333,6 +13341,9 @@
                 EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = mParser.getLong(
                         KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS,
                         DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
+                BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
+                        KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
+                        DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
             }
         }
 
@@ -13384,6 +13395,8 @@
             pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
             pw.print(KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS); pw.print("=");
             pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
+            pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
+            pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
         }
     }
 
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index eac9f64..cd80d53 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -23,6 +23,8 @@
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 
+import java.lang.ref.WeakReference;
+
 /**
  * Context for decor views which can be seeded with pure application context and not depend on the
  * activity, but still provide some of the facilities that Activity has,
@@ -35,9 +37,12 @@
     private WindowManager mWindowManager;
     private Resources mActivityResources;
 
-    public DecorContext(Context context, Resources activityResources) {
+    private WeakReference<Context> mActivityContext;
+
+    public DecorContext(Context context, Context activityContext) {
         super(context, null);
-        mActivityResources = activityResources;
+        mActivityContext = new WeakReference<>(activityContext);
+        mActivityResources = activityContext.getResources();
     }
 
     void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -60,6 +65,13 @@
 
     @Override
     public Resources getResources() {
+        Context activityContext = mActivityContext.get();
+        // Attempt to update the local cached Resources from the activity context. If the activity
+        // is no longer around, return the old cached values.
+        if (activityContext != null) {
+            mActivityResources = activityContext.getResources();
+        }
+
         return mActivityResources;
     }
 
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index eadefc9..a186f1a 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.policy;
 
+import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.WindowConfiguration;
 import android.graphics.Outline;
 import android.graphics.drawable.InsetDrawable;
@@ -41,7 +43,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -55,7 +56,6 @@
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -281,11 +281,16 @@
         initResizingPaints();
     }
 
-    void setBackgroundFallback(int resId) {
-        mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
+    void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
+        mBackgroundFallback.setDrawable(fallbackDrawable);
         setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
     }
 
+    @TestApi
+    public @Nullable Drawable getBackgroundFallback() {
+        return mBackgroundFallback.getDrawable();
+    }
+
     @Override
     public boolean gatherTransparentRegion(Region region) {
         boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
@@ -946,7 +951,7 @@
                         mWindow.isTranslucent() || mWindow.isShowingWallpaper());
             } else {
                 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
-                        getContext(), 0, mWindow.mBackgroundFallbackResource,
+                        mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable,
                         mWindow.isTranslucent() || mWindow.isShowingWallpaper());
             }
             if (mResizingBackgroundDrawable != null) {
@@ -1844,6 +1849,13 @@
     }
 
     @Override
+    public Resources getResources() {
+        // Make sure the Resources object is propogated from the Context since it can be updated in
+        // the Context object.
+        return getContext().getResources();
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
@@ -1901,9 +1913,9 @@
 
     private void loadBackgroundDrawablesIfNeeded() {
         if (mResizingBackgroundDrawable == null) {
-            mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
-                    mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource,
-                    mWindow.isTranslucent() || mWindow.isShowingWallpaper());
+            mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable,
+                    mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent()
+                    || mWindow.isShowingWallpaper());
             if (mResizingBackgroundDrawable == null) {
                 // We shouldn't really get here as the background fallback should be always
                 // available since it is defaulted by the system.
@@ -2011,20 +2023,14 @@
      * Returns the color used to fill areas the app has not rendered content to yet when the
      * user is resizing the window of an activity in multi-window mode.
      */
-    public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
-            int backgroundFallbackRes, boolean windowTranslucent) {
-        if (backgroundRes != 0) {
-            final Drawable drawable = context.getDrawable(backgroundRes);
-            if (drawable != null) {
-                return enforceNonTranslucentBackground(drawable, windowTranslucent);
-            }
+    public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable,
+            @Nullable Drawable fallbackDrawable, boolean windowTranslucent) {
+        if (backgroundDrawable != null) {
+            return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent);
         }
 
-        if (backgroundFallbackRes != 0) {
-            final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
-            if (fallbackDrawable != null) {
-                return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
-            }
+        if (fallbackDrawable != null) {
+            return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
         }
         return new ColorDrawable(Color.BLACK);
     }
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 3af3e2a..a3c7a9e 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -344,7 +344,7 @@
             if (dockedSide == DOCKED_LEFT) {
                 position += mInsets.left;
             } else if (dockedSide == DOCKED_RIGHT) {
-                position = mDisplayWidth - position - mInsets.right;
+                position = mDisplayWidth - position - mInsets.right - mDividerSize;
             }
         }
         mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f569132..64a8f9f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -218,10 +218,8 @@
 
     private ProgressBar mHorizontalProgressBar;
 
-    int mBackgroundResource = 0;
-    int mBackgroundFallbackResource = 0;
-
-    private Drawable mBackgroundDrawable;
+    Drawable mBackgroundDrawable = null;
+    Drawable mBackgroundFallbackDrawable = null;
 
     private boolean mLoadElevation = true;
     private float mElevation;
@@ -1471,14 +1469,14 @@
 
     @Override
     public final void setBackgroundDrawable(Drawable drawable) {
-        if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {
-            mBackgroundResource = 0;
+        if (drawable != mBackgroundDrawable) {
             mBackgroundDrawable = drawable;
             if (mDecor != null) {
                 mDecor.setWindowBackground(drawable);
-            }
-            if (mBackgroundFallbackResource != 0) {
-                mDecor.setBackgroundFallback(drawable != null ? 0 : mBackgroundFallbackResource);
+                if (mBackgroundFallbackDrawable != null) {
+                    mDecor.setBackgroundFallback(drawable != null ? null :
+                            mBackgroundFallbackDrawable);
+                }
             }
         }
     }
@@ -2300,7 +2298,7 @@
             if (applicationContext == null) {
                 context = getContext();
             } else {
-                context = new DecorContext(applicationContext, getContext().getResources());
+                context = new DecorContext(applicationContext, getContext());
                 if (mTheme != -1) {
                     context.setTheme(mTheme);
                 }
@@ -2511,19 +2509,18 @@
         // the values are inherited from our container.
         if (getContainer() == null) {
             if (mBackgroundDrawable == null) {
-                if (mBackgroundResource == 0) {
-                    mBackgroundResource = a.getResourceId(
-                            R.styleable.Window_windowBackground, 0);
-                }
+
                 if (mFrameResource == 0) {
                     mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                 }
-                mBackgroundFallbackResource = a.getResourceId(
-                        R.styleable.Window_windowBackgroundFallback, 0);
-                if (false) {
-                    System.out.println("Background: "
-                            + Integer.toHexString(mBackgroundResource) + " Frame: "
-                            + Integer.toHexString(mFrameResource));
+
+                if (a.hasValue(R.styleable.Window_windowBackground)) {
+                    mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);
+                }
+
+                if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {
+                    mBackgroundFallbackDrawable =
+                            a.getDrawable(R.styleable.Window_windowBackgroundFallback);
                 }
             }
             if (mLoadElevation) {
@@ -2618,13 +2615,7 @@
         // Remaining setup -- of background and title -- that only applies
         // to top-level windows.
         if (getContainer() == null) {
-            final Drawable background;
-            if (mBackgroundResource != 0) {
-                background = getContext().getDrawable(mBackgroundResource);
-            } else {
-                background = mBackgroundDrawable;
-            }
-            mDecor.setWindowBackground(background);
+            mDecor.setWindowBackground(mBackgroundDrawable);
 
             final Drawable frame;
             if (mFrameResource != 0) {
@@ -2734,8 +2725,8 @@
                 }
             }
 
-            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
-                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
+            if (mDecor.getBackground() == null && mBackgroundFallbackDrawable != null) {
+                mDecor.setBackgroundFallback(mBackgroundFallbackDrawable);
             }
 
             // Only inflate or create a new TransitionManager if the caller hasn't
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 497e7b0..54dede6 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -17,6 +17,7 @@
 package com.android.internal.view;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -30,6 +31,7 @@
 import android.view.ViewParent;
 import android.view.WindowManager;
 
+import android.widget.PopupWindow;
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 import com.android.internal.view.menu.MenuBuilder;
@@ -241,6 +243,23 @@
         }
     }
 
+    /**
+     * If this is set to true, the action mode view will dismiss itself on touch events outside of
+     * its window. This only makes sense if the action mode view is a PopupWindow that is touchable
+     * but not focusable, which means touches outside of the window will be delivered to the window
+     * behind. The default is false.
+     *
+     * This is for internal use only and the approach to this may change.
+     * @hide
+     *
+     * @param outsideTouchable whether or not this action mode is "outside touchable"
+     * @param onDismiss optional. Sets a callback for when this action mode popup dismisses itself
+     */
+    public void setOutsideTouchable(
+            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+        mFloatingToolbar.setOutsideTouchable(outsideTouchable, onDismiss);
+    }
+
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         mFloatingToolbarVisibilityHelper.setWindowFocused(hasWindowFocus);
diff --git a/core/java/com/android/internal/widget/BackgroundFallback.java b/core/java/com/android/internal/widget/BackgroundFallback.java
index 309f80c..f69aa4d 100644
--- a/core/java/com/android/internal/widget/BackgroundFallback.java
+++ b/core/java/com/android/internal/widget/BackgroundFallback.java
@@ -17,6 +17,7 @@
 
 package com.android.internal.widget;
 
+import android.annotation.Nullable;
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
@@ -35,6 +36,10 @@
         mBackgroundFallback = d;
     }
 
+    public @Nullable Drawable getDrawable() {
+        return mBackgroundFallback;
+    }
+
     public boolean hasFallback() {
         return mBackgroundFallback != null;
     }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index f70c554..42fb1f5 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -21,6 +21,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Color;
@@ -259,6 +260,22 @@
         return mPopup.isHidden();
     }
 
+    /**
+     * If this is set to true, the action mode view will dismiss itself on touch events outside of
+     * its window. If the toolbar is already showing, it will be re-shown so that this setting takes
+     * effect immediately.
+     *
+     * @param outsideTouchable whether or not this action mode is "outside touchable"
+     * @param onDismiss optional. Sets a callback for when this action mode popup dismisses itself
+     */
+    public void setOutsideTouchable(
+            boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+        if (mPopup.setOutsideTouchable(outsideTouchable, onDismiss) && isShowing()) {
+            dismiss();
+            doShow();
+        }
+    }
+
     private void doShow() {
         List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
         menuItems.sort(mMenuItemComparator);
@@ -513,6 +530,32 @@
         }
 
         /**
+         * Makes this toolbar "outside touchable" and sets the onDismissListener.
+         * This will take effect the next time the toolbar is re-shown.
+         *
+         * @param outsideTouchable if true, the popup will be made "outside touchable" and
+         *      "non focusable". The reverse will happen if false.
+         * @param onDismiss
+         *
+         * @return true if the "outsideTouchable" setting was modified. Otherwise returns false
+         *
+         * @see PopupWindow#setOutsideTouchable(boolean)
+         * @see PopupWindow#setFocusable(boolean)
+         * @see PopupWindow.OnDismissListener
+         */
+        public boolean setOutsideTouchable(
+                boolean outsideTouchable, @Nullable PopupWindow.OnDismissListener onDismiss) {
+            boolean ret = false;
+            if (mPopupWindow.isOutsideTouchable() ^ outsideTouchable) {
+                mPopupWindow.setOutsideTouchable(outsideTouchable);
+                mPopupWindow.setFocusable(!outsideTouchable);
+                ret = true;
+            }
+            mPopupWindow.setOnDismissListener(onDismiss);
+            return ret;
+        }
+
+        /**
          * Lays out buttons for the specified menu items.
          * Requires a subsequent call to {@link #show()} to show the items.
          */
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 5729b53..9048f87 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -77,7 +77,6 @@
         int otherViews = 0;
         int notGoneChildren = 0;
 
-        View lastNotGoneChild = null;
         for (int i = 0; i < N; i++) {
             View c = getChildAt(i);
             if (c instanceof TextView) {
@@ -87,7 +86,6 @@
             }
             if (c.getVisibility() != GONE) {
                 notGoneChildren++;
-                lastNotGoneChild = c;
             }
         }
 
@@ -107,11 +105,8 @@
                 }
             }
         }
-        boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
-        boolean singleChildCentered = notGoneChildren == 1 && centerAligned;
-        boolean needsRegularMeasurement = notGoneChildren > 1 || singleChildCentered;
 
-        if (needsRegularMeasurement && needRebuild) {
+        if (needRebuild) {
             rebuildMeasureOrder(textViews, otherViews);
         }
 
@@ -123,7 +118,7 @@
         int usedWidth = 0;
 
         int measuredChildren = 0;
-        for (int i = 0; i < N && needsRegularMeasurement; i++) {
+        for (int i = 0; i < N; i++) {
             // Measure shortest children first. To avoid measuring twice, we approximate by looking
             // at the text length.
             View c;
@@ -156,25 +151,6 @@
             measuredChildren++;
         }
 
-        // Make sure to measure the last child full-width if we didn't use up the entire width,
-        // or we didn't measure yet because there's just one child.
-        if (lastNotGoneChild != null && !centerAligned && (constrained && usedWidth < innerWidth
-                || notGoneChildren == 1)) {
-            MarginLayoutParams lp = (MarginLayoutParams) lastNotGoneChild.getLayoutParams();
-            if (notGoneChildren > 1) {
-                // Need to make room, since we already measured this once.
-                usedWidth -= lastNotGoneChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
-            }
-
-            int originalWidth = lp.width;
-            lp.width = LayoutParams.MATCH_PARENT;
-            measureChildWithMargins(lastNotGoneChild, widthMeasureSpec, usedWidth,
-                    heightMeasureSpec, 0 /* usedHeight */);
-            lp.width = originalWidth;
-
-            usedWidth += lastNotGoneChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
-        }
-
         mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft;
         setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                 resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index da494d4..21c769e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -726,7 +726,7 @@
         android:description="@string/permdesc_receiveMms"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to read previously received cell broadcast
+    <!-- @TestApi Allows an application to read previously received cell broadcast
          messages and to register a content observer to get notifications when
          a cell broadcast has been received and added to the database. For
          emergency alerts, the database is updated immediately after the
@@ -1424,6 +1424,13 @@
     <permission android:name="android.permission.NETWORK_SETTINGS"
         android:protectionLevel="signature" />
 
+    <!-- Allows SetupWizard to call methods in Networking services
+         <p>Not for use by any other third-party or privileged applications.
+         @hide This should only be used by SetupWizard.
+    -->
+    <permission android:name="android.permission.NETWORK_SETUP_WIZARD"
+        android:protectionLevel="signature|setup" />
+
     <!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.ACCESS_LOWPAN_STATE"
@@ -3324,7 +3331,7 @@
     <permission android:name="android.permission.OBSERVE_APP_USAGE"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide @SystemApi Allows an application to change the app idle state of an app.
+    <!-- @hide @TestApi @SystemApi Allows an application to change the app idle state of an app.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
         android:protectionLevel="signature|privileged" />
@@ -3550,7 +3557,7 @@
     <permission android:name="android.permission.UPDATE_LOCK"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi Allows an application to read the current set of notifications, including
+    <!-- @SystemApi @TestApi Allows an application to read the current set of notifications, including
          any metadata and intents attached.
          @hide -->
     <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
diff --git a/core/res/res/layout-television/autofill_save.xml b/core/res/res/layout-television/autofill_save.xml
new file mode 100644
index 0000000..ebd2dec
--- /dev/null
+++ b/core/res/res/layout-television/autofill_save.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- NOTE: outer layout is required to provide proper shadow. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:id="@+id/autofill_save"
+        android:background="?android:attr/colorBackground"
+        android:layout_marginTop="32dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="40dp"
+        android:paddingEnd="40dp"
+        android:paddingTop="40dp"
+        android:paddingBottom="40dp">
+
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginEnd="32dp">
+
+            <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="6dp">
+
+                <ImageView
+                    android:id="@+id/autofill_save_icon"
+                    android:scaleType="fitStart"
+                    android:layout_marginEnd="10dp"
+                    android:layout_gravity="center_vertical"
+                    android:layout_width="48dp"
+                    android:layout_height="48dp"/>
+                <TextView
+                    android:id="@+id/autofill_save_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/autofill_save_title"
+                    android:layout_gravity="center_vertical"
+                    android:textSize="24sp" />
+            </LinearLayout>
+
+            <com.android.server.autofill.ui.CustomScrollView
+                android:id="@+id/autofill_save_custom_subtitle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="4dp"
+                android:visibility="gone"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="304dp"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <Button
+                android:id="@+id/autofill_save_no"
+                style="?attr/borderlessButtonStyle"
+                android:textAlignment="viewStart"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/autofill_save_no">
+            </Button>
+
+            <Button
+                android:id="@+id/autofill_save_yes"
+                style="?attr/borderlessButtonStyle"
+                android:textAlignment="viewStart"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/autofill_save_yes">
+            </Button>
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index ef19f87..a88836e 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<view  xmlns:android="http://schemas.android.com/apk/res/android"
-    class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
+<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/autofill_dataset_picker"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
@@ -31,4 +30,4 @@
         android:visibility="gone">
     </ListView>
 
-</view>
+</FrameLayout>
diff --git a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
index 07298c1..1d2b5e5 100644
--- a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
+++ b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
@@ -14,35 +14,73 @@
      limitations under the License.
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/autofill_dataset_picker"
-    style="@style/AutofillDatasetPicker"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:background="?android:attr/windowBackground"
+    android:paddingStart="40dp"
+    android:paddingEnd="40dp"
+    android:paddingTop="40dp"
+    android:paddingBottom="40dp">
 
-    <TextView
-        android:layout_width="wrap_content"
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:text="@string/autofill_window_title"
-        android:layout_above="@+id/autofill_dataset_container"
-        android:layout_alignStart="@+id/autofill_dataset_container"
-        android:textSize="16sp"/>
+        android:layout_weight="1"
+        android:layout_marginEnd="32dp">
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="6dp"
+            >
+            <ImageView
+                android:id="@+id/autofill_dataset_icon"
+                android:scaleType="fitStart"
+                android:layout_marginEnd="10dp"
+                android:layout_gravity="center_vertical"
+                android:layout_width="48dp"
+                android:layout_height="48dp"/>
+            <TextView
+                android:id="@+id/autofill_dataset_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:textSize="24sp" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/autofill_dataset_header"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"/>
+
+    </LinearLayout>
 
     <!-- autofill_container is the common parent for inserting authentication item or
-         autofill_dataset_list-->
-    <FrameLayout
-        android:id="@+id/autofill_dataset_container"
-        android:layout_width="wrap_content"
+         autofill_dataset_list, autofill_dataset_foolter-->
+    <LinearLayout
+        android:layout_width="304dp"
         android:layout_height="wrap_content"
-        android:layout_centerInParent="true">
+        android:id="@+id/autofill_dataset_picker"
+        android:orientation="vertical">
         <ListView
             android:id="@+id/autofill_dataset_list"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:clickable="true"
             android:divider="@null"
             android:drawSelectorOnTop="true"
             android:visibility="gone"/>
-    </FrameLayout>
+        <LinearLayout
+            android:id="@+id/autofill_dataset_footer"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"/>
+    </LinearLayout>
 
-</RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 048494a..093f035 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<view  xmlns:android="http://schemas.android.com/apk/res/android"
-    class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
+<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/autofill_dataset_picker"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
@@ -54,4 +53,4 @@
 
     </LinearLayout>
 
-</view>
+</FrameLayout>
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
deleted file mode 100644
index 24b14a0..0000000
--- a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/autofill_dataset_picker"
-    style="@style/AutofillDatasetPicker"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/autofill_window_title"
-        android:layout_above="@+id/autofill_dataset_container"
-        android:layout_alignStart="@+id/autofill_dataset_container"
-        android:textSize="16sp"/>
-
-    <!-- autofill_container is the common parent for inserting authentication item or
-         autofill_dataset_list-->
-    <FrameLayout
-        android:id="@+id/autofill_dataset_container"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerInParent="true">
-
-        <LinearLayout
-            xmlns:android="http://schemas.android.com/apk/res/android"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-
-            <LinearLayout
-                android:id="@+id/autofill_dataset_header"
-                android:visibility="gone"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical"/>
-
-            <ListView
-                android:id="@+id/autofill_dataset_list"
-                android:layout_weight="1"
-                android:layout_width="fill_parent"
-                android:layout_height="0dp"
-                android:clickable="true"
-                android:divider="@null"
-                android:drawSelectorOnTop="true"
-                android:visibility="gone"/>
-
-            <LinearLayout
-                android:id="@+id/autofill_dataset_footer"
-                android:visibility="gone"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical"/>
-
-        </LinearLayout>
-
-    </FrameLayout>
-
-</RelativeLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 3a6f573..d1861d9 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -53,10 +53,10 @@
                 android:layout_marginTop="@dimen/notification_progress_margin_top"
                 android:layout_marginBottom="6dp"/>
             <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/notification_text_margin_top"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
                 android:singleLine="false"
                 android:gravity="top"
                 android:visibility="gone"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 23d8799..d6f0e1d 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -53,7 +53,7 @@
                 android:layout_marginTop="@dimen/notification_progress_margin_top"
                 android:layout_marginBottom="2dp"/>
             <TextView android:id="@+id/inbox_text0"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -62,7 +62,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text1"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -71,7 +71,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text2"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -80,7 +80,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text3"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -89,7 +89,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text4"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -98,7 +98,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text5"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
@@ -107,7 +107,7 @@
                 android:layout_weight="1"
                 />
             <TextView android:id="@+id/inbox_text6"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
+                style="@style/Widget.Material.Notification.Text"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index d2fd467..9bdc495 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -41,6 +41,7 @@
             android:id="@+id/group_message_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/notification_text_margin_top"
             android:spacing="2dp"
             android:layout_weight="1"/>
     </com.android.internal.widget.RemeasuringLinearLayout>
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index ca35de3..6459bb8 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -31,7 +31,7 @@
         android:textAlignment="viewStart"
         />
     <TextView android:id="@+id/text_line_1"
-        android:textAppearance="@style/TextAppearance.Material.Notification"
+        style="@style/Widget.Material.Notification.Text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="end|bottom"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 68f34c8..3b27666 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License
   -->
 <com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/Widget.Material.Notification.Text"
     android:id="@+id/text"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -25,5 +26,4 @@
     android:gravity="top"
     android:singleLine="true"
     android:textAlignment="viewStart"
-    android:textAppearance="@style/TextAppearance.Material.Notification"
     />
diff --git a/core/res/res/values-television/dimens.xml b/core/res/res/values-television/dimens.xml
index aa56251..4c25225 100644
--- a/core/res/res/values-television/dimens.xml
+++ b/core/res/res/values-television/dimens.xml
@@ -20,8 +20,4 @@
      <item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item>
      <item type="dimen" format="float" name="spot_shadow_alpha">0.3</item>
 
-     <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
-     <dimen name="autofill_dataset_picker_max_width">60%</dimen>
-     <dimen name="autofill_dataset_picker_max_height">70%</dimen>
-
 </resources>
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index e01caa3..e380a7b 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -16,4 +16,6 @@
 <resources>
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill" />
+    <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save" />
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 158d4e5..a83878b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -339,13 +339,13 @@
                   then in code manually set your window's background to
                   null so it will not be drawn.
              </ul> -->
-        <attr name="windowBackground" format="reference" />
+        <attr name="windowBackground" format="reference|color" />
         <!-- Drawable to draw selectively within the inset areas when the windowBackground
              has been set to null. This protects against seeing visual garbage in the
              surface when the app has not drawn any content into this area. One example is
              when the user is resizing a window of an activity that has
              {@link android.R.attr#resizeableActivity} set for multi-window mode. -->
-        <attr name="windowBackgroundFallback" format="reference" />
+        <attr name="windowBackgroundFallback" format="reference|color" />
         <!-- Drawable to use as a frame around the window. -->
         <attr name="windowFrame" format="reference" />
         <!-- Flag indicating whether there should be no title on this window. -->
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 3609fb8..2966aff 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -145,4 +145,8 @@
     <color name="datepicker_default_view_animator_color_material_light">#fff2f2f2</color>
     <color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
 
+    <!-- Autofill colors -->
+    <color name="autofill_background_material_dark">@color/material_blue_grey_900</color>
+    <color name="autofill_background_material_light">@color/material_grey_50</color>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 54864f3..5c80d4d2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -948,6 +948,16 @@
         <!-- B y-intercept --> <item>-0.198650895</item>
     </string-array>
 
+
+    <!-- Indicate available ColorDisplayController.COLOR_MODE_xxx. -->
+    <integer-array name="config_availableColorModes">
+        <!-- Example:
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        -->
+    </integer-array>
+
     <!-- Indicate whether to allow the device to suspend when the screen is off
          due to the proximity sensor.  This resource should only be set to true
          if the sensor HAL correctly handles the proximity sensor as a wake-up source.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 860dd87..c7b65ef 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -207,6 +207,9 @@
     <!-- The bottom padding for the notification header -->
     <dimen name="notification_header_padding_bottom">16dp</dimen>
 
+    <!-- The margin at the top of the notification header when dozing. -->
+    <dimen name="notification_header_margin_top_ambient">3dp</dimen>
+
     <!-- The margin at the bottom of the notification header. -->
     <dimen name="notification_header_margin_bottom">0dp</dimen>
 
@@ -629,7 +632,7 @@
     <!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
     <dimen name="notification_media_image_max_width_low_ram">100dp</dimen>
     <!-- The size of the right icon image when on low ram -->
-    <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size_low_ram</dimen>
+    <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
 
     <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e5bb587..b92052b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2196,8 +2196,8 @@
     <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] -->
     <string name="setup_autofill">Set up Autofill</string>
 
-    <!-- Title of fullscreen autofill window [CHAR-LIMIT=80] -->
-    <string name="autofill_window_title">Autofill</string>
+    <!-- Title of fullscreen autofill window, including the name of which autofill service it is using [CHAR-LIMIT=NONE] -->
+    <string name="autofill_window_title">Autofill with <xliff:g id="serviceName" example="MyPass">%1$s</xliff:g></string>
 
     <!-- String used to separate FirstName and LastName when writing out a local name
          e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 984461b..50a6ff3 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1487,13 +1487,19 @@
         <item name="successColor">@color/lock_pattern_view_success_color</item>
     </style>
 
-    <!-- @hide -->
+    <!-- @hide Autofill background for popup window (not for fullscreen) -->
     <style name="AutofillDatasetPicker">
         <item name="elevation">4dp</item>
         <item name="background">@drawable/autofill_dataset_picker_background</item>
     </style>
 
     <!-- @hide -->
+    <style name="AutofillHalfScreenAnimation">
+        <item name="android:windowEnterAnimation">@anim/slide_in_up</item>
+        <item name="android:windowExitAnimation">@anim/slide_out_down</item>
+    </style>
+
+    <!-- @hide -->
     <style name="AutofillSaveAnimation">
         <item name="android:windowEnterAnimation">@anim/slide_in_up</item>
         <item name="android:windowExitAnimation">@anim/slide_out_down</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index b8a046f..67b3c92 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -500,11 +500,15 @@
 
     <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
 
-    <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Light.TextView">
+    <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
+        <item name="lineHeight">20sp</item>
+        <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
+    </style>
+
+    <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Notification.Text">
         <item name="layout_width">wrap_content</item>
         <item name="layout_height">wrap_content</item>
-        <item name="ellipsize">end</item>
-        <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
+        <item name="ellipsize">end</item>z
     </style>
 
     <style name="Widget.Material.Notification.MessagingName" parent="Widget.Material.Light.TextView">
@@ -1305,6 +1309,7 @@
     </style>
 
     <style name="Notification.Header.Ambient">
+        <item name="layout_marginTop">@dimen/notification_header_margin_top_ambient</item>
         <item name="gravity">top|center_horizontal</item>
     </style>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d155771..5298e5b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -663,6 +663,7 @@
   <java-symbol type="string" name="autofill_state_re" />
   <java-symbol type="string" name="autofill_this_form" />
   <java-symbol type="string" name="autofill_username_re" />
+  <java-symbol type="string" name="autofill_window_title" />
   <java-symbol type="string" name="autofill_zip_4_re" />
   <java-symbol type="string" name="autofill_zip_code" />
   <java-symbol type="string" name="autofill_zip_code_re" />
@@ -2963,6 +2964,7 @@
   <java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
   <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
   <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
+  <java-symbol type="array" name="config_availableColorModes" />
 
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
@@ -3055,13 +3057,13 @@
   <java-symbol type="layout" name="autofill_dataset_picker"/>
   <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
   <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
-  <java-symbol type="layout" name="autofill_dataset_picker_header_footer_fullscreen"/>
   <java-symbol type="id" name="autofill" />
-  <java-symbol type="id" name="autofill_dataset_container"/>
   <java-symbol type="id" name="autofill_dataset_footer"/>
   <java-symbol type="id" name="autofill_dataset_header"/>
+  <java-symbol type="id" name="autofill_dataset_icon" />
   <java-symbol type="id" name="autofill_dataset_list"/>
   <java-symbol type="id" name="autofill_dataset_picker"/>
+  <java-symbol type="id" name="autofill_dataset_title" />
   <java-symbol type="id" name="autofill_save_custom_subtitle" />
   <java-symbol type="id" name="autofill_save_icon" />
   <java-symbol type="id" name="autofill_save_no" />
@@ -3086,6 +3088,7 @@
   <java-symbol type="string" name="autofill_save_type_email_address" />
   <java-symbol type="drawable" name="autofill_dataset_picker_background" />
   <java-symbol type="style" name="AutofillDatasetPicker" />
+  <java-symbol type="style" name="AutofillHalfScreenAnimation" />
   <java-symbol type="style" name="AutofillSaveAnimation" />
   <java-symbol type="dimen" name="autofill_dataset_picker_max_width"/>
   <java-symbol type="dimen" name="autofill_dataset_picker_max_height"/>
@@ -3093,6 +3096,9 @@
   <java-symbol type="dimen" name="autofill_save_icon_max_size"/>
   <java-symbol type="integer" name="autofill_max_visible_datasets" />
 
+  <java-symbol type="style" name="Theme.DeviceDefault.Autofill" />
+  <java-symbol type="style" name="Theme.DeviceDefault.Autofill.Save" />
+
   <java-symbol type="dimen" name="notification_big_picture_max_height"/>
   <java-symbol type="dimen" name="notification_big_picture_max_width"/>
   <java-symbol type="dimen" name="notification_media_image_max_width"/>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 92b2f33..14e5082 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1673,6 +1673,15 @@
         <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
+
+    <!-- @hide DeviceDefault theme for the autofill FillUi -->
+    <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill.Light">
+    </style>
+
+    <!-- @hide DeviceDefault theme for the autofill SaveUi -->
+    <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save.Light">
+    </style>
+
     <!-- DeviceDefault theme for the default system theme.  -->
     <style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" />
 
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 9b633fc..b3e33d5 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1417,4 +1417,25 @@
         <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
         <item name="colorSecondary">@color/secondary_material_settings</item>
     </style>
+
+    <!-- @hide -->
+    <style name="Theme.Material.Autofill" parent="Theme.Material">
+        <item name="colorBackground">@color/autofill_background_material_dark</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="Theme.Material.Autofill.Light" parent="Theme.Material.Light">
+        <item name="colorBackground">@color/autofill_background_material_light</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="Theme.Material.Autofill.Save" parent="Theme.Material.Panel">
+        <item name="colorBackground">@color/autofill_background_material_dark</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="Theme.Material.Autofill.Save.Light" parent="Theme.Material.Light.Panel">
+        <item name="colorBackground">@color/autofill_background_material_light</item>
+    </style>
+
 </resources>
diff --git a/core/tests/coretests/res/layout/activity_text_view.xml b/core/tests/coretests/res/layout/activity_text_view.xml
index d5be87d..0568683 100644
--- a/core/tests/coretests/res/layout/activity_text_view.xml
+++ b/core/tests/coretests/res/layout/activity_text_view.xml
@@ -31,6 +31,8 @@
     <TextView
         android:id="@+id/nonselectable_textview"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:focusable="false"
+        android:focusableInTouchMode="false" />
 
 </LinearLayout>
diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml
index bcde4c1..dbc4626 100644
--- a/core/tests/coretests/res/values/styles.xml
+++ b/core/tests/coretests/res/values/styles.xml
@@ -31,4 +31,12 @@
     <style name="LayoutInDisplayCutoutModeNever">
         <item name="android:windowLayoutInDisplayCutoutMode">never</item>
     </style>
+    <style name="WindowBackgroundColorLiteral">
+        <item name="android:windowBackground">#00FF00</item>
+    </style>
+    <style name="WindowBackgroundFallbackColorLiteral">
+        <item name="android:windowBackground">@null</item>
+        <item name="android:colorBackground">@null</item>
+        <item name="android:windowBackgroundFallback">#0000FF</item>
+    </style>
 </resources>
diff --git a/core/tests/coretests/src/android/app/usage/EventListTest.java b/core/tests/coretests/src/android/app/usage/EventListTest.java
new file mode 100644
index 0000000..9dc0d43
--- /dev/null
+++ b/core/tests/coretests/src/android/app/usage/EventListTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EventListTest {
+    private static final String TAG = EventListTest.class.getSimpleName();
+
+    private UsageEvents.Event getUsageEvent(long timeStamp) {
+        final UsageEvents.Event event = new UsageEvents.Event();
+        event.mTimeStamp = timeStamp;
+        return event;
+    }
+
+    private static String getListTimeStamps(EventList list) {
+        final StringBuilder builder = new StringBuilder("[");
+        for (int i = 0; i < list.size() - 1; i++) {
+            builder.append(list.get(i).mTimeStamp);
+            builder.append(", ");
+        }
+        builder.append(list.get(list.size() - 1).mTimeStamp);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    private static void assertSorted(EventList eventList) {
+        for (int i = 1; i < eventList.size(); i++) {
+            final long lastTimeStamp = eventList.get(i - 1).mTimeStamp;
+            if (eventList.get(i).mTimeStamp < lastTimeStamp) {
+                Log.e(TAG, "Unsorted timestamps in list: " + getListTimeStamps(eventList));
+                fail("Timestamp " + eventList.get(i).mTimeStamp + " at " + i
+                        + " follows larger timestamp " + lastTimeStamp);
+            }
+        }
+    }
+
+    @Test
+    public void testInsertsSortedRandom() {
+        final Random random = new Random(128);
+        final EventList listUnderTest = new EventList();
+        for (int i = 0; i < 100; i++) {
+            listUnderTest.insert(getUsageEvent(random.nextLong()));
+        }
+        assertSorted(listUnderTest);
+    }
+
+    @Test
+    public void testInsertsSortedWithDuplicates() {
+        final Random random = new Random(256);
+        final EventList listUnderTest = new EventList();
+        for (int i = 0; i < 10; i++) {
+            final long randomTimeStamp = random.nextLong();
+            for (int j = 0; j < 10; j++) {
+                listUnderTest.insert(getUsageEvent(randomTimeStamp));
+            }
+        }
+        assertSorted(listUnderTest);
+    }
+
+    @Test
+    public void testFirstIndexOnOrAfter() {
+        final EventList listUnderTest = new EventList();
+        listUnderTest.insert(getUsageEvent(2));
+        listUnderTest.insert(getUsageEvent(5));
+        listUnderTest.insert(getUsageEvent(5));
+        listUnderTest.insert(getUsageEvent(5));
+        listUnderTest.insert(getUsageEvent(8));
+        assertTrue(listUnderTest.firstIndexOnOrAfter(1) == 0);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(2) == 0);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(3) == 1);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(4) == 1);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(5) == 1);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(6) == 4);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(7) == 4);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(8) == 4);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(9) == listUnderTest.size());
+        assertTrue(listUnderTest.firstIndexOnOrAfter(100) == listUnderTest.size());
+
+        listUnderTest.clear();
+        assertTrue(listUnderTest.firstIndexOnOrAfter(5) == 0);
+        assertTrue(listUnderTest.firstIndexOnOrAfter(100) == 0);
+    }
+
+    @Test
+    public void testClear() {
+        final EventList listUnderTest = new EventList();
+        for (int i = 1; i <= 100; i++) {
+            listUnderTest.insert(getUsageEvent(i));
+        }
+        listUnderTest.clear();
+        assertEquals(0, listUnderTest.size());
+    }
+
+    @Test
+    public void testSize() {
+        final EventList listUnderTest = new EventList();
+        for (int i = 1; i <= 100; i++) {
+            listUnderTest.insert(getUsageEvent(i));
+        }
+        assertEquals(100, listUnderTest.size());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/usage/TimeSparseArrayTest.java b/core/tests/coretests/src/android/app/usage/TimeSparseArrayTest.java
deleted file mode 100644
index db46740..0000000
--- a/core/tests/coretests/src/android/app/usage/TimeSparseArrayTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.app.usage;
-
-import static org.junit.Assert.assertTrue;
-
-import android.os.SystemClock;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TimeSparseArrayTest {
-    @Test
-    public void testDuplicateKeysNotDropped() {
-        final TimeSparseArray<Integer> testTimeSparseArray = new TimeSparseArray<>();
-        final long key = SystemClock.elapsedRealtime();
-        for (int i = 0; i < 5; i++) {
-            testTimeSparseArray.put(key, i);
-        }
-        for (int i = 0; i < 5; i++) {
-            final int valueIndex = testTimeSparseArray.indexOfValue(i);
-            assertTrue("Value " + i + " not found; intended key: " + key , valueIndex >= 0);
-            final long keyForValue = testTimeSparseArray.keyAt(valueIndex);
-            assertTrue("Value " + i + " stored too far (at " + keyForValue + ") from intended key "
-                    + key, Math.abs(keyForValue - key) < 100);
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index ff3452c..0fd0136 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -28,7 +28,6 @@
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
@@ -40,7 +39,6 @@
 import java.util.Objects;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class CursorAnchorInfoTest {
     private static final float EPSILON = 0.0000001f;
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 52df338..9edbf3e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,7 +26,6 @@
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.LargeTest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith;
 
 @LargeTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InputMethodInfoTest {
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
index ff71c00..8df1848 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -30,7 +29,6 @@
 import java.util.ArrayList;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InputMethodSubtypeArrayTest {
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
index 2c85ce7..c76359e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -35,7 +34,6 @@
 import java.util.Objects;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InputMethodSubtypeTest {
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
index c7b5933..8c96b58 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
@@ -23,7 +23,6 @@
 
 import android.graphics.RectF;
 import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
@@ -34,7 +33,6 @@
 import java.util.Objects;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class SparseRectFArrayTest {
     // A test data for {@link SparseRectFArray}. null represents the gap of indices.
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index b0593d2..e891fc9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -18,11 +18,22 @@
 
 import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.LocaleList;
 import android.service.textclassifier.TextClassifierService;
 import android.support.test.InstrumentationRegistry;
@@ -35,6 +46,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -305,7 +317,6 @@
     public void testGetLocalTextClassifier() {
         assertTrue(mTcm.getTextClassifier(TextClassifier.LOCAL) instanceof TextClassifierImpl);
     }
-
     @Test
     public void testGetSystemTextClassifier() {
         assertTrue(
@@ -313,6 +324,48 @@
                 || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
     }
 
+    @Test
+    public void testCannotResolveIntent() {
+        final PackageManager fakePackageMgr = mock(PackageManager.class);
+
+        ResolveInfo validInfo = mContext.getPackageManager().resolveActivity(
+                new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0);
+        // Make packageManager fail when it gets the following intent:
+        ArgumentMatcher<Intent> toFailIntent =
+                intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT);
+
+        when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo);
+        when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null);
+
+        ContextWrapper fakeContext = new ContextWrapper(mContext) {
+            @Override
+            public PackageManager getPackageManager() {
+                return fakePackageMgr;
+            }
+        };
+
+        TextClassifier fallback = TextClassifier.NO_OP;
+        TextClassifier classifier = new TextClassifierImpl(
+                fakeContext, TextClassificationConstants.loadFromString(null), fallback);
+
+        String text = "Contact me at +12122537077";
+        String classifiedText = "+12122537077";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification result = classifier.classifyText(request);
+        TextClassification fallbackResult = fallback.classifyText(request);
+
+        // classifier should not totally fail in which case it returns a fallback result.
+        // It should skip the failing intent and return a result for non-failing intents.
+        assertFalse(result.getActions().isEmpty());
+        assertNotSame(result, fallbackResult);
+    }
+
     private boolean isTextClassifierDisabled() {
         return mClassifier == TextClassifier.NO_OP;
     }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 320a7ac..bcb7cf3 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -318,40 +318,52 @@
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
         sleepForFloatingToolbarPopup();
         assertFloatingToolbarIsDisplayed();
-
     }
 
     @Test
     public void testToolbarAppearsAfterLinkClickedNonselectable() throws Throwable {
-        TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
-        int position = (textLink.getStart() + textLink.getEnd()) / 2;
+        final TextView textView = mActivity.findViewById(R.id.nonselectable_textview);
+        final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
+        final int position = (textLink.getStart() + textLink.getEnd()) / 2;
+
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
         sleepForFloatingToolbarPopup();
         assertFloatingToolbarIsDisplayed();
+        assertTrue(textView.hasSelection());
+
+        // toggle
+        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
+        sleepForFloatingToolbarPopup();
+        assertFalse(textView.hasSelection());
+
+        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarIsDisplayed();
+        assertTrue(textView.hasSelection());
+
+        // click outside
+        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(0));
+        sleepForFloatingToolbarPopup();
+        assertFalse(textView.hasSelection());
     }
 
     @Test
     public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable {
-        // Add a link to both selectable and nonselectable TextViews:
-        TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
-        int selectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
-        textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
-        int nonselectablePosition = (textLink.getStart() + textLink.getEnd()) / 2;
-        TextView selectableTextView = mActivity.findViewById(R.id.textview);
-        TextView nonselectableTextView = mActivity.findViewById(R.id.nonselectable_textview);
+        final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
+        final int position = (textLink.getStart() + textLink.getEnd()) / 2;
+        final TextView textView = mActivity.findViewById(R.id.nonselectable_textview);
+        mActivityRule.runOnUiThread(() -> textView.setFocusableInTouchMode(true));
 
-        onView(withId(R.id.nonselectable_textview))
-                .perform(clickOnTextAtIndex(nonselectablePosition));
+        onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
         sleepForFloatingToolbarPopup();
         assertFloatingToolbarIsDisplayed();
-        assertTrue(nonselectableTextView.hasSelection());
+        assertTrue(textView.hasSelection());
 
-        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(selectablePosition));
+        mActivityRule.runOnUiThread(() -> textView.clearFocus());
+        mInstrumentation.waitForIdleSync();
         sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
 
-        assertTrue(selectableTextView.hasSelection());
-        assertFalse(nonselectableTextView.hasSelection());
+        assertFalse(textView.hasSelection());
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 0429d99..13d6749 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -23,7 +23,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.inputmethod.InputMethodInfo;
@@ -41,7 +40,6 @@
 import java.util.List;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InputMethodSubtypeSwitchingControllerTest {
     private static final String DUMMY_PACKAGE_NAME = "dummy package name";
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index a49e203..3064afa 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -37,7 +37,6 @@
 import android.os.Build;
 import android.os.LocaleList;
 import android.os.Parcel;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -54,7 +53,6 @@
 import java.util.Objects;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InputMethodUtilsTest {
     private static final boolean IS_AUX = true;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
index 1269ffd..549511a 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.LocaleList;
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -30,7 +29,6 @@
 import java.util.Locale;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class LocaleUtilsTest {
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 36e54ad..b68f6b1 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -190,6 +190,11 @@
         @Override
         public void cancelCpuSyncDueToWakelockChange() {
         }
+
+        @Override
+        public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
+            return null;
+        }
     }
 }
 
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index 518cadd..85b210b 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -24,6 +24,9 @@
 import static org.junit.Assert.assertThat;
 
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -89,6 +92,32 @@
                 is(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER));
     }
 
+    @Test
+    public void testWindowBackground_colorLiteral() {
+        createPhoneWindowWithTheme(R.style.WindowBackgroundColorLiteral);
+        installDecor();
+
+        Drawable backgroundDrawable = mPhoneWindow.getDecorView().getBackground();
+        assertThat(backgroundDrawable instanceof ColorDrawable, is(true));
+
+        ColorDrawable colorDrawable = (ColorDrawable) backgroundDrawable;
+        assertThat(colorDrawable.getColor(), is(Color.GREEN));
+    }
+
+    @Test
+    public void testWindowBackgroundFallback_colorLiteral() {
+        createPhoneWindowWithTheme(R.style.WindowBackgroundFallbackColorLiteral);
+        installDecor();
+
+        DecorView decorView = (DecorView) mPhoneWindow.getDecorView();
+        Drawable fallbackDrawable = decorView.getBackgroundFallback();
+
+        assertThat(fallbackDrawable instanceof ColorDrawable, is(true));
+
+        ColorDrawable colorDrawable = (ColorDrawable) fallbackDrawable;
+        assertThat(colorDrawable.getColor(), is(Color.BLUE));
+    }
+
     private void createPhoneWindowWithTheme(int theme) {
         mPhoneWindow = new PhoneWindow(new ContextThemeWrapper(mContext, theme));
     }
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index bf91a16..6d5276f 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -118,7 +118,7 @@
 
     @Test
     public void installPlatformSignedFrameworkOverlayAndUpdate() throws Exception {
-        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectAppResource"));
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectFrameworkResource"));
 
         installPackage("OverlayHostTests_FrameworkOverlayV1.apk");
         setOverlayEnabled(FRAMEWORK_OVERLAY_PACKAGE_NAME, true);
@@ -138,6 +138,27 @@
                 "expectFrameworkOverlayV2Resource"));
     }
 
+    @Test
+    public void enabledFrameworkOverlayMustAffectNewlyInstalledPackage() throws Exception {
+        try {
+            setPackageEnabled(DEVICE_TEST_PKG, false);
+
+            installPackage("OverlayHostTests_FrameworkOverlayV1.apk");
+            setOverlayEnabled(FRAMEWORK_OVERLAY_PACKAGE_NAME, true);
+            assertTrue(overlayManagerContainsPackage(FRAMEWORK_OVERLAY_PACKAGE_NAME));
+
+            setPackageEnabled(DEVICE_TEST_PKG, true);
+            assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+                    "expectFrameworkOverlayV1Resource"));
+        } finally {
+            setPackageEnabled(DEVICE_TEST_PKG, true);
+        }
+    }
+
+    private void setPackageEnabled(String pkg, boolean enabled) throws Exception {
+        getDevice().executeShellCommand("cmd package " + (enabled ? "enable " : "disable ") + pkg);
+    }
+
     private void setOverlayEnabled(String pkg, boolean enabled) throws Exception {
         getDevice().executeShellCommand("cmd overlay " + (enabled ? "enable " : "disable ") + pkg);
     }
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index d46bb37..a174d77 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -61,7 +61,7 @@
     }
 
     @Test
-    public void expectFrameworkOverlayResource() throws Exception {
+    public void expectFrameworkResource() throws Exception {
         assertEquals("OK", mResources.getString(android.R.string.ok));
     }
 
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 46a7fa8..066e17c 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -371,9 +371,9 @@
      *     a private key; used only to call startActivity(); must not
      *     be null.
      * @param response Callback to invoke when the request completes;
-     *     must not be null
+     *     must not be null.
      * @param keyTypes The acceptable types of asymmetric keys such as
-     *     "RSA" or "DSA", or a null array.
+     *     "RSA" or "DSA", or null.
      * @param issuers The acceptable certificate issuers for the
      *     certificate matching the private key, or null.
      * @param host The host name of the server requesting the
@@ -385,7 +385,8 @@
      */
     public static void choosePrivateKeyAlias(@NonNull Activity activity,
             @NonNull KeyChainAliasCallback response,
-            @KeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
+            @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes,
+            @Nullable Principal[] issuers,
             @Nullable String host, int port, @Nullable String alias) {
         Uri uri = null;
         if (host != null) {
@@ -421,9 +422,9 @@
      *     a private key; used only to call startActivity(); must not
      *     be null.
      * @param response Callback to invoke when the request completes;
-     *     must not be null
+     *     must not be null.
      * @param keyTypes The acceptable types of asymmetric keys such as
-     *     "EC" or "RSA", or a null array.
+     *     "EC" or "RSA", or null.
      * @param issuers The acceptable certificate issuers for the
      *     certificate matching the private key, or null.
      * @param uri The full URI the server is requesting the certificate
@@ -433,7 +434,8 @@
      */
     public static void choosePrivateKeyAlias(@NonNull Activity activity,
             @NonNull KeyChainAliasCallback response,
-            @KeyProperties.KeyAlgorithmEnum String[] keyTypes, Principal[] issuers,
+            @Nullable @KeyProperties.KeyAlgorithmEnum String[] keyTypes,
+            @Nullable Principal[] issuers,
             @Nullable Uri uri, @Nullable String alias) {
         /*
          * TODO currently keyTypes, issuers are unused. They are meant
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7d4e6f8..b3a3f45 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3504,13 +3504,14 @@
 {
     PackageGroup(
             ResTable* _owner, const String16& _name, uint32_t _id,
-            bool appAsLib, bool _isSystemAsset)
+            bool appAsLib, bool _isSystemAsset, bool _isDynamic)
         : owner(_owner)
         , name(_name)
         , id(_id)
         , largestTypeId(0)
         , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
         , isSystemAsset(_isSystemAsset)
+        , isDynamic(_isDynamic)
     { }
 
     ~PackageGroup() {
@@ -3614,6 +3615,7 @@
     // If the package group comes from a system asset. Used in
     // determining non-system locales.
     const bool                      isSystemAsset;
+    const bool isDynamic;
 };
 
 ResTable::Theme::Theme(const ResTable& table)
@@ -3982,6 +3984,11 @@
     return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
 }
 
+inline ssize_t ResTable::getResourcePackageIndexFromPackage(uint8_t packageID) const
+{
+    return ((ssize_t)mPackageMap[packageID])-1;
+}
+
 status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
     return addInternal(data, size, NULL, 0, false, cookie, copyData);
 }
@@ -4037,7 +4044,7 @@
     for (size_t i=0; i < src->mPackageGroups.size(); i++) {
         PackageGroup* srcPg = src->mPackageGroups[i];
         PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id,
-                false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset);
+                false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset, srcPg->isDynamic);
         for (size_t j=0; j<srcPg->packages.size(); j++) {
             pg->packages.add(srcPg->packages[j]);
         }
@@ -6277,6 +6284,68 @@
     return true;
 }
 
+bool ResTable::isPackageDynamic(uint8_t packageID) const {
+  if (mError != NO_ERROR) {
+      return false;
+  }
+  if (packageID == 0) {
+      ALOGW("Invalid package number 0x%08x", packageID);
+      return false;
+  }
+
+  const ssize_t p = getResourcePackageIndexFromPackage(packageID);
+
+  if (p < 0) {
+      ALOGW("Unknown package number 0x%08x", packageID);
+      return false;
+  }
+
+  const PackageGroup* const grp = mPackageGroups[p];
+  if (grp == NULL) {
+      ALOGW("Bad identifier for package number 0x%08x", packageID);
+      return false;
+  }
+
+  return grp->isDynamic;
+}
+
+bool ResTable::isResourceDynamic(uint32_t resID) const {
+    if (mError != NO_ERROR) {
+        return false;
+    }
+
+    const ssize_t p = getResourcePackageIndex(resID);
+    const int t = Res_GETTYPE(resID);
+    const int e = Res_GETENTRY(resID);
+
+    if (p < 0) {
+        if (Res_GETPACKAGE(resID)+1 == 0) {
+            ALOGW("No package identifier for resource number 0x%08x", resID);
+        } else {
+            ALOGW("No known package for resource number 0x%08x", resID);
+        }
+        return false;
+    }
+    if (t < 0) {
+        ALOGW("No type identifier for resource number 0x%08x", resID);
+        return false;
+    }
+
+    const PackageGroup* const grp = mPackageGroups[p];
+    if (grp == NULL) {
+        ALOGW("Bad identifier for resource number 0x%08x", resID);
+        return false;
+    }
+
+    Entry entry;
+    status_t err = getEntry(grp, t, e, NULL, &entry);
+    if (err != NO_ERROR) {
+        return false;
+    }
+
+    return grp->isDynamic;
+}
+
 static bool keyCompare(const ResTable_sparseTypeEntry& entry , uint16_t entryIdx) {
   return dtohs(entry.idx) < entryIdx;
 }
@@ -6520,12 +6589,14 @@
         id = targetPackageId;
     }
 
+    bool isDynamic = false;
     if (id >= 256) {
         LOG_ALWAYS_FATAL("Package id out of range");
         return NO_ERROR;
     } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
         // This is a library or a system asset, so assign an ID
         id = mNextPackageId++;
+        isDynamic = true;
     }
 
     PackageGroup* group = NULL;
@@ -6553,10 +6624,9 @@
     size_t idx = mPackageMap[id];
     if (idx == 0) {
         idx = mPackageGroups.size() + 1;
-
         char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
         strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
-        group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset);
+        group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset, isDynamic);
         if (group == NULL) {
             delete package;
             return (mError=NO_MEMORY);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index bc0a07a..63b9e3a 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1716,6 +1716,18 @@
     bool getResourceFlags(uint32_t resID, uint32_t* outFlags) const;
 
     /**
+     * Returns whether or not the package for the given resource has been dynamically assigned.
+     * If the resource can't be found, returns 'false'.
+     */
+    bool isResourceDynamic(uint32_t resID) const;
+
+    /**
+     * Returns whether or not the given package has been dynamically assigned.
+     * If the package can't be found, returns 'false'.
+     */
+    bool isPackageDynamic(uint8_t packageID) const;
+
+    /**
      * Retrieve the value of a resource.  If the resource is found, returns a
      * value >= 0 indicating the table it is in (for use with
      * getTableStringBlock() and getTableCookie()) and fills in 'outValue'.  If
@@ -2024,6 +2036,7 @@
             bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
 
     ssize_t getResourcePackageIndex(uint32_t resID) const;
+    ssize_t getResourcePackageIndexFromPackage(uint8_t packageID) const;
 
     status_t getEntry(
         const PackageGroup* packageGroup, int typeIndex, int entryIndex,
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index be7d663..569de76 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -40,7 +40,6 @@
 }
 
 DeferredLayerUpdater::~DeferredLayerUpdater() {
-    SkSafeUnref(mColorFilter);
     setTransform(nullptr);
     mRenderState.unregisterDeferredLayerUpdater(this);
     destroyLayer();
@@ -67,8 +66,11 @@
 void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
     mAlpha = PaintUtils::getAlphaDirect(paint);
     mMode = PaintUtils::getBlendModeDirect(paint);
-    SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
-    SkRefCnt_SafeAssign(mColorFilter, colorFilter);
+    if (paint) {
+        mColorFilter = paint->refColorFilter();
+    } else {
+        mColorFilter.reset();
+    }
 }
 
 void DeferredLayerUpdater::apply() {
@@ -143,7 +145,7 @@
 #endif
         mSurfaceTexture->getTransformMatrix(transform);
 
-        updateLayer(forceFilter, transform);
+        updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
     }
 }
 
@@ -153,17 +155,19 @@
                         Layer::Api::OpenGL, Layer::Api::Vulkan);
 
     static const mat4 identityMatrix;
-    updateLayer(false, identityMatrix.data);
+    updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN);
 
     VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
     vkLayer->updateTexture();
 }
 
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform,
+                                       android_dataspace dataspace) {
     mLayer->setBlend(mBlend);
     mLayer->setForceFilter(forceFilter);
     mLayer->setSize(mWidth, mHeight);
     mLayer->getTexTransform().load(textureTransform);
+    mLayer->setDataSpace(dataspace);
 }
 
 void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9dc029f..fe3ee7a 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -20,6 +20,7 @@
 #include <SkMatrix.h>
 #include <cutils/compiler.h>
 #include <gui/GLConsumer.h>
+#include <system/graphics.h>
 #include <utils/StrongPointer.h>
 
 #include <GLES2/gl2.h>
@@ -41,7 +42,7 @@
     // Note that DeferredLayerUpdater assumes it is taking ownership of the layer
     // and will not call incrementRef on it as a result.
     typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth,
-                                 uint32_t layerHeight, SkColorFilter* colorFilter, int alpha,
+                                 uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha,
                                  SkBlendMode mode, bool blend)>
             CreateLayerFn;
     ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
@@ -96,7 +97,7 @@
 
     void detachSurfaceTexture();
 
-    void updateLayer(bool forceFilter, const float* textureTransform);
+    void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace);
 
     void destroyLayer();
 
@@ -109,7 +110,7 @@
     int mWidth = 0;
     int mHeight = 0;
     bool mBlend = false;
-    SkColorFilter* mColorFilter = nullptr;
+    sk_sp<SkColorFilter> mColorFilter;
     int mAlpha = 255;
     SkBlendMode mMode = SkBlendMode::kSrcOver;
     sp<GLConsumer> mSurfaceTexture;
diff --git a/libs/hwui/EglReadback.cpp b/libs/hwui/EglReadback.cpp
index a836afe..2d5367b 100644
--- a/libs/hwui/EglReadback.cpp
+++ b/libs/hwui/EglReadback.cpp
@@ -58,7 +58,7 @@
 CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
                                                  Matrix4& texTransform, const Rect& srcRect,
                                                  SkBitmap* bitmap) {
-    mRenderThread.eglManager().initialize();
+    mRenderThread.requireGlContext();
     // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
     // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
     // to be able to properly sample from the buffer.
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
index 32a3014..42ae29d 100644
--- a/libs/hwui/GlLayer.cpp
+++ b/libs/hwui/GlLayer.cpp
@@ -32,7 +32,7 @@
 namespace uirenderer {
 
 GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
-                 SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
+                 sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
         : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode)
         , caches(Caches::getInstance())
         , texture(caches) {
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
index 1b09191..28749a0 100644
--- a/libs/hwui/GlLayer.h
+++ b/libs/hwui/GlLayer.h
@@ -32,7 +32,7 @@
 class GlLayer : public Layer {
 public:
     GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
-            SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend);
+            sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend);
     virtual ~GlLayer();
 
     uint32_t getWidth() const override { return texture.mWidth; }
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 86950d5..fb8f033 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,34 +18,58 @@
 
 #include "renderstate/RenderState.h"
 
-#include <SkColorFilter.h>
+#include <SkToSRGBColorFilter.h>
 
 namespace android {
 namespace uirenderer {
 
-Layer::Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
+Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha,
              SkBlendMode mode)
         : GpuMemoryTracker(GpuObjectType::Layer)
         , mRenderState(renderState)
         , mApi(api)
-        , colorFilter(nullptr)
+        , mColorFilter(colorFilter)
         , alpha(alpha)
         , mode(mode) {
     // TODO: This is a violation of Android's typical ref counting, but it
     // preserves the old inc/dec ref locations. This should be changed...
     incStrong(nullptr);
-
+    buildColorSpaceWithFilter();
     renderState.registerLayer(this);
 }
 
 Layer::~Layer() {
-    SkSafeUnref(colorFilter);
-
     mRenderState.unregisterLayer(this);
 }
 
-void Layer::setColorFilter(SkColorFilter* filter) {
-    SkRefCnt_SafeAssign(colorFilter, filter);
+void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
+    if (filter != mColorFilter) {
+        mColorFilter = filter;
+        buildColorSpaceWithFilter();
+    }
+}
+
+void Layer::setDataSpace(android_dataspace dataspace) {
+    if (dataspace != mCurrentDataspace) {
+        mCurrentDataspace = dataspace;
+        buildColorSpaceWithFilter();
+    }
+}
+
+void Layer::buildColorSpaceWithFilter() {
+    sk_sp<SkColorFilter> colorSpaceFilter;
+    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
+    if (colorSpace && !colorSpace->isSRGB()) {
+        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+    }
+
+    if (mColorFilter && colorSpaceFilter) {
+        mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
+    } else if (colorSpaceFilter) {
+        mColorSpaceWithFilter = colorSpaceFilter;
+    } else {
+        mColorSpaceWithFilter = mColorFilter;
+    }
 }
 
 void Layer::postDecStrong() {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 6921381..89bcddc 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -19,6 +19,8 @@
 #include <GpuMemoryTracker.h>
 #include <utils/RefBase.h>
 
+#include <SkColorFilter.h>
+#include <SkColorSpace.h>
 #include <SkBlendMode.h>
 #include <SkPaint.h>
 
@@ -72,9 +74,15 @@
 
     inline SkBlendMode getMode() const { return mode; }
 
-    inline SkColorFilter* getColorFilter() const { return colorFilter; }
+    inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
 
-    void setColorFilter(SkColorFilter* filter);
+    void setColorFilter(sk_sp<SkColorFilter> filter);
+
+    void setDataSpace(android_dataspace dataspace);
+
+    void setColorSpace(sk_sp<SkColorSpace> colorSpace);
+
+    inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
 
     inline mat4& getTexTransform() { return texTransform; }
 
@@ -87,18 +95,30 @@
     void postDecStrong();
 
 protected:
-    Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
+    Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha,
           SkBlendMode mode);
 
     RenderState& mRenderState;
 
 private:
+    void buildColorSpaceWithFilter();
+
     Api mApi;
 
     /**
      * Color filter used to draw this layer. Optional.
      */
-    SkColorFilter* colorFilter;
+    sk_sp<SkColorFilter> mColorFilter;
+
+    /**
+     * Colorspace of the contents of the layer. Optional.
+     */
+    android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
+
+    /**
+     * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
+     */
+    sk_sp<SkColorFilter> mColorSpaceWithFilter;
 
     /**
      * Indicates raster data backing the layer is scaled, requiring filtration.
diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h
index f23f472..e9664d0 100644
--- a/libs/hwui/VkLayer.h
+++ b/libs/hwui/VkLayer.h
@@ -28,7 +28,7 @@
 class VkLayer : public Layer {
 public:
     VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
-            SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
+            sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
             : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode)
             , mWidth(layerWidth)
             , mHeight(layerHeight)
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 896c01c..00ba7130 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -50,9 +50,13 @@
         GlLayer* glLayer = static_cast<GlLayer*>(layer);
         GrGLTextureInfo externalTexture;
         externalTexture.fTarget = glLayer->getRenderTarget();
-        SkASSERT(GL_RGBA == glLayer->getTexture().internalFormat());
-        externalTexture.fFormat = GL_RGBA8;
         externalTexture.fID = glLayer->getTextureId();
+        // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't
+        // expose that info we use it as our default.  Further, given that we only use this texture
+        // as a source this will not impact how Skia uses the texture.  The only potential affect
+        // this is anticipated to have is that for some format types if we are not bound as an OES
+        // texture we may get invalid results for SKP capture if we read back the texture.
+        externalTexture.fFormat = GL_RGBA8;
         GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture);
         layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
                                               kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
@@ -83,7 +87,7 @@
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
         paint.setBlendMode(layer->getMode());
-        paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
+        paint.setColorFilter(layer->getColorSpaceWithFilter());
 
         const bool nonIdentityMatrix = !matrix.isIdentity();
         if (nonIdentityMatrix) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index b7aa78b..cfcfd2b 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -158,7 +158,7 @@
 }
 
 static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
-                          SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+                          sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) {
     GlLayer* layer =
             new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
     layer->generateTexture();
@@ -166,7 +166,7 @@
 }
 
 DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
-    mEglManager.initialize();
+    mRenderThread.requireGlContext();
     return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
 }
 
@@ -184,6 +184,7 @@
     }
 
     if (surface) {
+        mRenderThread.requireGlContext();
         const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
         mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
     }
@@ -221,24 +222,6 @@
 
 #define FENCE_TIMEOUT 2000000000
 
-class AutoEglFence {
-public:
-    AutoEglFence(EGLDisplay display) : mDisplay(display) {
-        fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
-    }
-
-    ~AutoEglFence() {
-        if (fence != EGL_NO_SYNC_KHR) {
-            eglDestroySyncKHR(mDisplay, fence);
-        }
-    }
-
-    EGLSyncKHR fence = EGL_NO_SYNC_KHR;
-
-private:
-    EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
 class AutoEglImage {
 public:
     AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
@@ -272,73 +255,102 @@
     GLuint mTexture = 0;
 };
 
-sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
-                                                         SkBitmap& skBitmap) {
-    renderThread.eglManager().initialize();
+struct FormatInfo {
 
-    sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
-    const SkImageInfo& info = skBitmap.info();
     PixelFormat pixelFormat;
     GLint format, type;
     bool isSupported = false;
+    bool valid = true;
+};
 
+static bool gpuSupportsHalfFloatTextures(renderthread::RenderThread& renderThread) {
+    static bool isSupported = renderThread.queue().runSync([&renderThread]() -> bool {
+        renderThread.requireGlContext();
+        sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
+        return grContext->colorTypeSupportedAsImage(kRGBA_F16_SkColorType);
+    });
+    return isSupported;
+}
+
+static FormatInfo determineFormat(renderthread::RenderThread& renderThread,
+                                  const SkBitmap& skBitmap) {
+    FormatInfo formatInfo;
     // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
-    switch (info.colorType()) {
+    switch (skBitmap.info().colorType()) {
         case kRGBA_8888_SkColorType:
-            isSupported = true;
+            formatInfo.isSupported = true;
         // ARGB_4444 is upconverted to RGBA_8888
         case kARGB_4444_SkColorType:
-            pixelFormat = PIXEL_FORMAT_RGBA_8888;
-            format = GL_RGBA;
-            type = GL_UNSIGNED_BYTE;
+            formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+            formatInfo.format = GL_RGBA;
+            formatInfo.type = GL_UNSIGNED_BYTE;
             break;
         case kRGBA_F16_SkColorType:
-            isSupported = grContext->colorTypeSupportedAsImage(kRGBA_F16_SkColorType);
-            if (isSupported) {
-                type = GL_HALF_FLOAT;
-                pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+            formatInfo.isSupported = gpuSupportsHalfFloatTextures(renderThread);
+            if (formatInfo.isSupported) {
+                formatInfo.type = GL_HALF_FLOAT;
+                formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
             } else {
-                type = GL_UNSIGNED_BYTE;
-                pixelFormat = PIXEL_FORMAT_RGBA_8888;
+                formatInfo.type = GL_UNSIGNED_BYTE;
+                formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
             }
-            format = GL_RGBA;
+            formatInfo.format = GL_RGBA;
             break;
         case kRGB_565_SkColorType:
-            isSupported = true;
-            pixelFormat = PIXEL_FORMAT_RGB_565;
-            format = GL_RGB;
-            type = GL_UNSIGNED_SHORT_5_6_5;
+            formatInfo.isSupported = true;
+            formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
+            formatInfo.format = GL_RGB;
+            formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
             break;
         case kGray_8_SkColorType:
-            isSupported = true;
-            pixelFormat = PIXEL_FORMAT_RGBA_8888;
-            format = GL_LUMINANCE;
-            type = GL_UNSIGNED_BYTE;
+            formatInfo.isSupported = true;
+            formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+            formatInfo.format = GL_LUMINANCE;
+            formatInfo.type = GL_UNSIGNED_BYTE;
             break;
         default:
-            ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
-            return nullptr;
+            ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
+            formatInfo.valid = false;
     }
+    return formatInfo;
+}
 
-    SkBitmap bitmap;
-    if (isSupported) {
-        bitmap = skBitmap;
+static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
+    if (format.isSupported) {
+        return source;
     } else {
+        SkBitmap bitmap;
+        const SkImageInfo& info = source.info();
         bitmap.allocPixels(
                 SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
         bitmap.eraseColor(0);
         if (info.colorType() == kRGBA_F16_SkColorType) {
             // Drawing RGBA_F16 onto ARGB_8888 is not supported
-            skBitmap.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
-                                bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+            source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
+                              bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
         } else {
             SkCanvas canvas(bitmap);
-            canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
+            canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
         }
+        return bitmap;
+    }
+}
+
+sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& thread,
+                                                         const SkBitmap& sourceBitmap) {
+    ATRACE_CALL();
+
+    LOG_ALWAYS_FATAL_IF(thread.isCurrent(), "Must not be called on RenderThread");
+
+    FormatInfo format = determineFormat(thread, sourceBitmap);
+    if (!format.valid) {
+        return nullptr;
     }
 
+    SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
     sp<GraphicBuffer> buffer = new GraphicBuffer(
-            info.width(), info.height(), pixelFormat,
+            static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
+            format.pixelFormat,
             GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
                     GraphicBuffer::USAGE_SW_READ_NEVER,
             std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) +
@@ -350,8 +362,11 @@
         return nullptr;
     }
 
-    // upload the bitmap into a texture
-    EGLDisplay display = eglGetCurrentDisplay();
+    EGLDisplay display = thread.queue().runSync([&]() -> EGLDisplay {
+        thread.requireGlContext();
+        return eglGetCurrentDisplay();
+    });
+
     LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
                         uirenderer::renderthread::EglManager::eglErrorString());
     // We use an EGLImage to access the content of the GraphicBuffer
@@ -363,35 +378,40 @@
               uirenderer::renderthread::EglManager::eglErrorString());
         return nullptr;
     }
-    AutoSkiaGlTexture glTexture;
-    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
-    GL_CHECKPOINT(MODERATE);
 
-    // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we provide.
-    // But asynchronous in sense that driver may upload texture onto hardware buffer when we first
-    // use it in drawing
-    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info.width(), info.height(), format, type,
-                    bitmap.getPixels());
-    GL_CHECKPOINT(MODERATE);
+    {
+        ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
+        EGLSyncKHR fence = thread.queue().runSync([&]() -> EGLSyncKHR {
+            thread.requireGlContext();
+            sk_sp<GrContext> grContext = sk_ref_sp(thread.getGrContext());
+            AutoSkiaGlTexture glTexture;
+            glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+            GL_CHECKPOINT(MODERATE);
 
-    // The fence is used to wait for the texture upload to finish
-    // properly. We cannot rely on glFlush() and glFinish() as
-    // some drivers completely ignore these API calls
-    AutoEglFence autoFence(display);
-    if (autoFence.fence == EGL_NO_SYNC_KHR) {
-        LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError());
-        return nullptr;
+            // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
+            // provide.
+            // But asynchronous in sense that driver may upload texture onto hardware buffer when we
+            // first
+            // use it in drawing
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format,
+                            format.type, bitmap.getPixels());
+            GL_CHECKPOINT(MODERATE);
+
+            EGLSyncKHR uploadFence =
+                    eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+            LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x",
+                                eglGetError());
+            glFlush();
+            grContext->resetContext(kTextureBinding_GrGLBackendState);
+            return uploadFence;
+        });
+
+        EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
+        LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+                            "Failed to wait for the fence %#x", eglGetError());
+
+        eglDestroySyncKHR(display, fence);
     }
-    // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
-    // pipeline flush (similar to what a glFlush() would do.)
-    EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
-                                             EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
-    if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
-        LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
-        return nullptr;
-    }
-
-    grContext->resetContext(kTextureBinding_GrGLBackendState);
 
     return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
 }
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 5e013b6..ef5d934 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -49,8 +49,10 @@
     bool isContextReady() override;
 
     static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+
+    // May be called by any thread except RenderThread.
     static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
-                                                SkBitmap& skBitmap);
+                                                const SkBitmap& skBitmap);
 
 private:
     renderthread::EglManager& mEglManager;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 9e73046..d66cba1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -147,6 +147,7 @@
             GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
             if (cachedContext.get() != currentContext) {
                 if (cachedContext.get()) {
+                    ATRACE_NAME("flush layers (context changed)");
                     cachedContext->flush();
                 }
                 cachedContext.reset(SkSafeRef(currentContext));
@@ -155,6 +156,7 @@
     }
 
     if (cachedContext.get()) {
+        ATRACE_NAME("flush layers");
         cachedContext->flush();
     }
 }
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 6530074..5825060 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,7 +115,8 @@
 }
 
 static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
-                          SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+                          sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
+                          bool blend) {
     return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 95ca8d9..a36dae4 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -552,7 +552,7 @@
     ATRACE_CALL();
     if (level >= TRIM_MEMORY_COMPLETE) {
         thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
-        thread.eglManager().destroy();
+        thread.destroyGlContext();
         thread.vulkanManager().destroy();
     } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
         thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 6e239e3..cd21822 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,27 +16,19 @@
 
 #include "EglManager.h"
 
-#include <string>
-
 #include <cutils/properties.h>
 #include <log/log.h>
+#include <utils/Trace.h>
 #include "utils/StringUtils.h"
 
-#include "Caches.h"
 #include "DeviceInfo.h"
 #include "Frame.h"
 #include "Properties.h"
-#include "RenderThread.h"
-#include "Texture.h"
-#include "renderstate/RenderState.h"
 
 #include <EGL/eglext.h>
-#include <GrContextOptions.h>
-#include <gl/GrGLInterface.h>
 
-#ifdef HWUI_GLES_WRAP_ENABLED
-#include "debug/GlesDriver.h"
-#endif
+#include <string>
+#include <vector>
 
 #define GLES_VERSION 2
 
@@ -83,17 +75,21 @@
     bool glColorSpace = false;
     bool scRGB = false;
     bool contextPriority = false;
+    bool surfacelessContext = false;
 } EglExtensions;
 
-EglManager::EglManager(RenderThread& thread)
-        : mRenderThread(thread)
-        , mEglDisplay(EGL_NO_DISPLAY)
+EglManager::EglManager()
+        : mEglDisplay(EGL_NO_DISPLAY)
         , mEglConfig(nullptr)
         , mEglConfigWideGamut(nullptr)
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
         , mCurrentSurface(EGL_NO_SURFACE) {}
 
+EglManager::~EglManager() {
+    destroy();
+}
+
 void EglManager::initialize() {
     if (hasEglContext()) return;
 
@@ -126,26 +122,8 @@
     loadConfigs();
     createContext();
     createPBufferSurface();
-    makeCurrent(mPBufferSurface);
+    makeCurrent(mPBufferSurface, nullptr, /* force */ true);
     DeviceInfo::initialize();
-    mRenderThread.renderState().onGLContextCreated();
-
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
-#ifdef HWUI_GLES_WRAP_ENABLED
-        debug::GlesDriver* driver = debug::GlesDriver::get();
-        sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
-#else
-        sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
-#endif
-        LOG_ALWAYS_FATAL_IF(!glInterface.get());
-
-        GrContextOptions options;
-        options.fDisableDistanceFieldPaths = true;
-        mRenderThread.cacheManager().configureContext(&options);
-        sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
-        LOG_ALWAYS_FATAL_IF(!grContext.get());
-        mRenderThread.setGrContext(grContext);
-    }
 }
 
 void EglManager::initExtensions() {
@@ -170,6 +148,7 @@
     EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
 #endif
     EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
+    EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
 }
 
 bool EglManager::hasEglContext() {
@@ -195,7 +174,7 @@
                         EGL_CONFIG_CAVEAT,
                         EGL_NONE,
                         EGL_STENCIL_SIZE,
-                        Stencil::getStencilSize(),
+                        STENCIL_BUFFER_SIZE,
                         EGL_SURFACE_TYPE,
                         EGL_WINDOW_BIT | swapBehavior,
                         EGL_NONE};
@@ -232,7 +211,7 @@
                                EGL_DEPTH_SIZE,
                                0,
                                EGL_STENCIL_SIZE,
-                               Stencil::getStencilSize(),
+                               STENCIL_BUFFER_SIZE,
                                EGL_SURFACE_TYPE,
                                EGL_WINDOW_BIT | swapBehavior,
                                EGL_NONE};
@@ -269,14 +248,14 @@
     LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
                         "usePBufferSurface() called on uninitialized GlobalContext!");
 
-    if (mPBufferSurface == EGL_NO_SURFACE) {
+    if (mPBufferSurface == EGL_NO_SURFACE && !EglExtensions.surfacelessContext) {
         EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
         mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
     }
 }
 
 EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
-    initialize();
+    LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
 
     wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB &&
                      EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
@@ -350,10 +329,10 @@
 void EglManager::destroy() {
     if (mEglDisplay == EGL_NO_DISPLAY) return;
 
-    mRenderThread.setGrContext(nullptr);
-    mRenderThread.renderState().onGLContextDestroyed();
     eglDestroyContext(mEglDisplay, mEglContext);
-    eglDestroySurface(mEglDisplay, mPBufferSurface);
+    if (mPBufferSurface != EGL_NO_SURFACE) {
+        eglDestroySurface(mEglDisplay, mPBufferSurface);
+    }
     eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(mEglDisplay);
     eglReleaseThread();
@@ -364,8 +343,8 @@
     mCurrentSurface = EGL_NO_SURFACE;
 }
 
-bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
-    if (isCurrent(surface)) return false;
+bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut, bool force) {
+    if (!force && isCurrent(surface)) return false;
 
     if (surface == EGL_NO_SURFACE) {
         // Ensure we always have a valid surface & context
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index ef9effb..ca6d1b8 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -33,8 +33,12 @@
 // and EGLConfig, which are re-used by CanvasContext
 class EglManager {
 public:
+    explicit EglManager();
+
+    ~EglManager();
+
     static const char* eglErrorString();
-    // Returns true on success, false on failure
+
     void initialize();
 
     bool hasEglContext();
@@ -46,7 +50,7 @@
 
     bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
     // Returns true if the current surface changed, false if it was already current
-    bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr);
+    bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr, bool force = false);
     Frame beginFrame(EGLSurface surface);
     void damageFrame(const Frame& frame, const SkRect& dirty);
     // If this returns true it is mandatory that swapBuffers is called
@@ -61,10 +65,6 @@
     void fence();
 
 private:
-    friend class RenderThread;
-    explicit EglManager(RenderThread& thread);
-    // EglContext is never destroyed, method is purposely not implemented
-    ~EglManager();
 
     void initExtensions();
     void createPBufferSurface();
@@ -72,8 +72,6 @@
     void createContext();
     EGLint queryBufferAge(EGLSurface surface);
 
-    RenderThread& mRenderThread;
-
     EGLDisplay mEglDisplay;
     EGLConfig mEglConfig;
     EGLConfig mEglConfigWideGamut;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 4b948f8..0caf59b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,7 @@
 #include "Properties.h"
 #include "Readback.h"
 #include "Rect.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/VectorDrawableAtlas.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/CanvasContext.h"
@@ -323,7 +324,13 @@
 
 sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
     auto& thread = RenderThread::getInstance();
-    return thread.queue().runSync([&]() -> auto { return thread.allocateHardwareBitmap(bitmap); });
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+        return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(thread, bitmap);
+    } else {
+        return thread.queue().runSync([&]() -> auto {
+            return thread.allocateHardwareBitmap(bitmap);
+        });
+    }
 }
 
 int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 5e067da..3eaf43b 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -24,12 +24,19 @@
 #include "hwui/Bitmap.h"
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaOpenGLReadback.h"
-#include "pipeline/skia/SkiaVulkanReadback.h"
 #include "pipeline/skia/SkiaVulkanPipeline.h"
+#include "pipeline/skia/SkiaVulkanReadback.h"
 #include "renderstate/RenderState.h"
 #include "utils/FatVector.h"
 #include "utils/TimeUtils.h"
 
+#ifdef HWUI_GLES_WRAP_ENABLED
+#include "debug/GlesDriver.h"
+#endif
+
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
+
 #include <gui/DisplayEventReceiver.h>
 #include <sys/resource.h>
 #include <utils/Condition.h>
@@ -91,14 +98,11 @@
     DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
 
     virtual void requestNextVsync() override {
-        mRenderThread->queue().postDelayed(16_ms, [this]() {
-            mRenderThread->drainDisplayEventQueue();
-        });
+        mRenderThread->queue().postDelayed(16_ms,
+                                           [this]() { mRenderThread->drainDisplayEventQueue(); });
     }
 
-    virtual nsecs_t latestVsyncEvent() override {
-        return systemTime(CLOCK_MONOTONIC);
-    }
+    virtual nsecs_t latestVsyncEvent() override { return systemTime(CLOCK_MONOTONIC); }
 
 private:
     RenderThread* mRenderThread;
@@ -145,13 +149,13 @@
         auto receiver = std::make_unique<DisplayEventReceiver>();
         status_t status = receiver->initCheck();
         LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                "Initialization of DisplayEventReceiver "
-                        "failed with status: %d",
-                status);
+                            "Initialization of DisplayEventReceiver "
+                            "failed with status: %d",
+                            status);
 
         // Register the FD
         mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
-                RenderThread::displayEventReceiverCallback, this);
+                       RenderThread::displayEventReceiverCallback, this);
         mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
     } else {
         mVsyncSource = new DummyVsyncSource(this);
@@ -163,12 +167,43 @@
     nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
     mTimeLord.setFrameInterval(frameIntervalNanos);
     initializeDisplayEventReceiver();
-    mEglManager = new EglManager(*this);
+    mEglManager = new EglManager();
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager(*this);
     mCacheManager = new CacheManager(mDisplayInfo);
 }
 
+void RenderThread::requireGlContext() {
+    if (mEglManager->hasEglContext()) {
+        return;
+    }
+    mEglManager->initialize();
+    renderState().onGLContextCreated();
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+    debug::GlesDriver* driver = debug::GlesDriver::get();
+    sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
+#else
+    sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+#endif
+    LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+    GrContextOptions options;
+    options.fDisableDistanceFieldPaths = true;
+    cacheManager().configureContext(&options);
+    sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
+    LOG_ALWAYS_FATAL_IF(!grContext.get());
+    setGrContext(grContext);
+}
+
+void RenderThread::destroyGlContext() {
+    if (mEglManager->hasEglContext()) {
+        setGrContext(nullptr);
+        renderState().onGLContextDestroyed();
+        mEglManager->destroy();
+    }
+}
+
 void RenderThread::dumpGraphicsMemory(int fd) {
     globalProfileData()->dump(fd);
 
@@ -332,8 +367,6 @@
 sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
     auto renderType = Properties::getRenderPipelineType();
     switch (renderType) {
-        case RenderPipelineType::SkiaGL:
-            return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
         case RenderPipelineType::SkiaVulkan:
             return skiapipeline::SkiaVulkanPipeline::allocateHardwareBitmap(*this, skBitmap);
         default:
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 689f518..4a1fd9e 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -103,6 +103,9 @@
     sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
     void dumpGraphicsMemory(int fd);
 
+    void requireGlContext();
+    void destroyGlContext();
+
     /**
      * isCurrent provides a way to query, if the caller is running on
      * the render thread.
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 16c5afd..a7ac930 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,7 +72,7 @@
     layerUpdater->setTransform(&transform);
 
     // updateLayer so it's ready to draw
-    layerUpdater->updateLayer(true, Matrix4::identity().data);
+    layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN);
     if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
         static_cast<GlLayer*>(layerUpdater->backingLayer())
                 ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
@@ -140,7 +140,7 @@
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
         renderThread.vulkanManager().initialize();
     } else {
-        renderThread.eglManager().initialize();
+        renderThread.requireGlContext();
     }
 
     rtCallback(renderThread);
@@ -149,7 +149,7 @@
         renderThread.vulkanManager().destroy();
     } else {
         renderThread.renderState().flush(Caches::FlushMode::Full);
-        renderThread.eglManager().destroy();
+        renderThread.destroyGlContext();
     }
 }
 
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index b8b5050..f29830f 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -44,7 +44,7 @@
     // push the deferred updates to the layer
     Matrix4 scaledMatrix;
     scaledMatrix.loadScale(0.5, 0.5, 0.0);
-    layerUpdater->updateLayer(true, scaledMatrix.data);
+    layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN);
     if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
         GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
         glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 9bfb082..08b9679 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@
 // current thread can spoof being a GPU thread
 static void destroyEglContext() {
     if (TestUtils::isRenderThreadRunning()) {
-        TestUtils::runOnRenderThread([](RenderThread& thread) { thread.eglManager().destroy(); });
+        TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
     }
 }
 
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index c2af867..75740e8 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,6 +16,8 @@
 
 #include "Color.h"
 
+
+#include <utils/Log.h>
 #include <cmath>
 
 namespace android {
@@ -53,5 +55,57 @@
     return false;
 }
 
+sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
+
+    SkColorSpace::Gamut gamut;
+    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            gamut = SkColorSpace::kSRGB_Gamut;
+            break;
+        case HAL_DATASPACE_STANDARD_BT2020:
+            gamut = SkColorSpace::kRec2020_Gamut;
+            break;
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            gamut = SkColorSpace::kDCIP3_D65_Gamut;
+            break;
+        case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+            gamut = SkColorSpace::kAdobeRGB_Gamut;
+            break;
+        case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+            return nullptr;
+        case HAL_DATASPACE_STANDARD_BT601_625:
+        case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+        case HAL_DATASPACE_STANDARD_BT601_525:
+        case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+        case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+        case HAL_DATASPACE_STANDARD_BT470M:
+        case HAL_DATASPACE_STANDARD_FILM:
+        default:
+            ALOGW("Unsupported Gamut: %d", dataspace);
+            return nullptr;
+    }
+
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
+        case HAL_DATASPACE_TRANSFER_SRGB:
+            return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
+        case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+            return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+        case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+            return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+        case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+            return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+        case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
+            return nullptr;
+        case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+        case HAL_DATASPACE_TRANSFER_ST2084:
+        case HAL_DATASPACE_TRANSFER_HLG:
+        default:
+            ALOGW("Unsupported Gamma: %d", dataspace);
+            return nullptr;
+    }
+}
+
 };  // namespace uirenderer
 };  // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 7ac0d96..2bec1f5 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
 #define COLOR_H
 
 #include <math.h>
+#include <system/graphics.h>
 
 #include <SkColor.h>
 #include <SkColorSpace.h>
@@ -111,6 +112,8 @@
 // approximated with the native sRGB transfer function. This method
 // returns true for sRGB, gamma 2.2 and Display P3 for instance
 bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
+
+sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
 } /* namespace uirenderer */
 } /* namespace android */
 
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index a36bf80..44bc97a 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -12,9 +12,8 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-cc_library {
-    name: "libprotoutil",
+cc_defaults {
+    name: "libprotoutil_defaults",
 
     cflags: [
         "-Wall",
@@ -30,8 +29,6 @@
         "src/protobuf.cpp",
     ],
 
-    export_include_dirs: ["include"],
-
     shared_libs: [
         "libbase",
         "libcutils",
@@ -39,19 +36,32 @@
     ],
 }
 
+cc_library {
+    name: "libprotoutil",
+
+    defaults: ["libprotoutil_defaults"],
+
+    export_include_dirs: ["include"],
+}
+
 cc_test {
     name: "libprotoutil_test",
 
-    srcs: [
-        "tests/EncodedBuffer_test.cpp",
-    ],
+    defaults: ["libprotoutil_defaults"],
+
+    local_include_dirs: ["include"],
+
+    srcs: ["tests/*"],
 
     shared_libs: [
-        "libcutils",
-        "libprotoutil",
+        "libprotobuf-cpp-full",
     ],
 
     static_libs: [
         "libgmock",
     ],
+
+    proto: {
+        type: "full",
+    }
 }
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index e17f48b..ad76559 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -17,10 +17,11 @@
 #ifndef ANDROID_UTIL_PROTOOUTPUT_STREAM_H
 #define ANDROID_UTIL_PROTOOUTPUT_STREAM_H
 
-#include <android/util/EncodedBuffer.h>
-
+#include <cstdint>
 #include <string>
 
+#include <android/util/EncodedBuffer.h>
+
 namespace android {
 namespace util {
 
@@ -104,7 +105,7 @@
     /**
      * Starts a sub-message write session.
      * Returns a token of this write session.
-     * Must call end(token) when finish write this sub-message.
+     * Must call end(token) exactly once when finish write this sub-message.
      */
     uint64_t start(uint64_t fieldId);
     void end(uint64_t token);
@@ -143,16 +144,16 @@
 
     inline void writeDoubleImpl(uint32_t id, double val);
     inline void writeFloatImpl(uint32_t id, float val);
-    inline void writeInt64Impl(uint32_t id, long long val);
-    inline void writeInt32Impl(uint32_t id, int val);
+    inline void writeInt64Impl(uint32_t id, int64_t val);
+    inline void writeInt32Impl(uint32_t id, int32_t val);
     inline void writeUint64Impl(uint32_t id, uint64_t val);
     inline void writeUint32Impl(uint32_t id, uint32_t val);
     inline void writeFixed64Impl(uint32_t id, uint64_t val);
     inline void writeFixed32Impl(uint32_t id, uint32_t val);
-    inline void writeSFixed64Impl(uint32_t id, long long val);
-    inline void writeSFixed32Impl(uint32_t id, int val);
-    inline void writeZigzagInt64Impl(uint32_t id, long long val);
-    inline void writeZigzagInt32Impl(uint32_t id, int val);
+    inline void writeSFixed64Impl(uint32_t id, int64_t val);
+    inline void writeSFixed32Impl(uint32_t id, int32_t val);
+    inline void writeZigzagInt64Impl(uint32_t id, int64_t val);
+    inline void writeZigzagInt32Impl(uint32_t id, int32_t val);
     inline void writeEnumImpl(uint32_t id, int val);
     inline void writeBoolImpl(uint32_t id, bool val);
     inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size);
@@ -161,6 +162,9 @@
     bool compact();
     size_t editEncodedSize(size_t rawSize);
     bool compactSize(size_t rawSize);
+
+    template<typename T>
+    bool internalWrite(uint64_t fieldId, T val, const char* typeName);
 };
 
 }
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 3fee7e4..ff3fad6 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -15,7 +15,8 @@
  */
 #define LOG_TAG "libprotoutil"
 
-#include <inttypes.h>
+#include <cinttypes>
+#include <type_traits>
 
 #include <android-base/file.h>
 #include <android/util/protobuf.h>
@@ -51,112 +52,73 @@
     mExpectedObjectToken = UINT64_C(-1);
 }
 
+template<typename T>
 bool
-ProtoOutputStream::write(uint64_t fieldId, double val)
+ProtoOutputStream::internalWrite(uint64_t fieldId, T val, const char* typeName)
 {
     if (mCompact) return false;
     const uint32_t id = (uint32_t)fieldId;
     switch (fieldId & FIELD_TYPE_MASK) {
         case FIELD_TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
         case FIELD_TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
-        case FIELD_TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
+        case FIELD_TYPE_INT64:    writeInt64Impl(id, (int64_t)val);           break;
         case FIELD_TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
-        case FIELD_TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
+        case FIELD_TYPE_INT32:    writeInt32Impl(id, (int32_t)val);           break;
         case FIELD_TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
         case FIELD_TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
         case FIELD_TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
-        case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
-        case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
-        case FIELD_TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
-        case FIELD_TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
+        case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int32_t)val);        break;
+        case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (int64_t)val);        break;
+        case FIELD_TYPE_SINT32:   writeZigzagInt32Impl(id, (int32_t)val);     break;
+        case FIELD_TYPE_SINT64:   writeZigzagInt64Impl(id, (int64_t)val);     break;
+        case FIELD_TYPE_ENUM:
+            if (std::is_integral<T>::value) {
+                writeEnumImpl(id, (int)val);
+            } else {
+                goto unsupported;
+            }
+            break;
+        case FIELD_TYPE_BOOL:
+            if (std::is_integral<T>::value) {
+                writeBoolImpl(id, val != 0);
+            } else {
+                goto unsupported;
+            }
+            break;
         default:
-            ALOGW("Field type %d is not supported when writing double val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
-            return false;
+            goto unsupported;
     }
     return true;
+
+unsupported:
+    ALOGW("Field type %" PRIu64 " is not supported when writing %s val.",
+            (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT, typeName);
+    return false;
 }
 
 bool
+ProtoOutputStream::write(uint64_t fieldId, double val)
+{
+    return internalWrite(fieldId, val, "double");
+}
+
+
+bool
 ProtoOutputStream::write(uint64_t fieldId, float val)
 {
-    if (mCompact) return false;
-    const uint32_t id = (uint32_t)fieldId;
-    switch (fieldId & FIELD_TYPE_MASK) {
-        case FIELD_TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
-        case FIELD_TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
-        case FIELD_TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
-        case FIELD_TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
-        case FIELD_TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
-        case FIELD_TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
-        case FIELD_TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
-        case FIELD_TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
-        case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
-        case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
-        case FIELD_TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
-        case FIELD_TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
-        default:
-            ALOGW("Field type %d is not supported when writing float val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
-            return false;
-    }
-    return true;
+    return internalWrite(fieldId, val, "float");
 }
 
 bool
 ProtoOutputStream::write(uint64_t fieldId, int val)
 {
-    if (mCompact) return false;
-    const uint32_t id = (uint32_t)fieldId;
-    switch (fieldId & FIELD_TYPE_MASK) {
-        case FIELD_TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
-        case FIELD_TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
-        case FIELD_TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
-        case FIELD_TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
-        case FIELD_TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
-        case FIELD_TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
-        case FIELD_TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
-        case FIELD_TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
-        case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
-        case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
-        case FIELD_TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
-        case FIELD_TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
-        case FIELD_TYPE_ENUM:     writeEnumImpl(id, (int)val);                break;
-        case FIELD_TYPE_BOOL:     writeBoolImpl(id, val != 0);                break;
-        default:
-            ALOGW("Field type %d is not supported when writing int val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
-            return false;
-    }
-    return true;
+    return internalWrite(fieldId, val, "int");
 }
 
 bool
 ProtoOutputStream::write(uint64_t fieldId, long long val)
 {
-    if (mCompact) return false;
-    const uint32_t id = (uint32_t)fieldId;
-    switch (fieldId & FIELD_TYPE_MASK) {
-        case FIELD_TYPE_DOUBLE:   writeDoubleImpl(id, (double)val);           break;
-        case FIELD_TYPE_FLOAT:    writeFloatImpl(id, (float)val);             break;
-        case FIELD_TYPE_INT64:    writeInt64Impl(id, (long long)val);         break;
-        case FIELD_TYPE_UINT64:   writeUint64Impl(id, (uint64_t)val);         break;
-        case FIELD_TYPE_INT32:    writeInt32Impl(id, (int)val);               break;
-        case FIELD_TYPE_FIXED64:  writeFixed64Impl(id, (uint64_t)val);        break;
-        case FIELD_TYPE_FIXED32:  writeFixed32Impl(id, (uint32_t)val);        break;
-        case FIELD_TYPE_UINT32:   writeUint32Impl(id, (uint32_t)val);         break;
-        case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val);            break;
-        case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val);      break;
-        case FIELD_TYPE_SINT32:   writeZigzagInt32Impl(id, (int)val);         break;
-        case FIELD_TYPE_SINT64:   writeZigzagInt64Impl(id, (long long)val);   break;
-        case FIELD_TYPE_ENUM:     writeEnumImpl(id, (int)val);                break;
-        case FIELD_TYPE_BOOL:     writeBoolImpl(id, val != 0);                break;
-        default:
-            ALOGW("Field type %d is not supported when writing long long val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
-            return false;
-    }
-    return true;
+    return internalWrite(fieldId, val, "long long");
 }
 
 bool
@@ -169,8 +131,8 @@
             writeBoolImpl(id, val);
             return true;
         default:
-            ALOGW("Field type %d is not supported when writing bool val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            ALOGW("Field type %" PRIu64 " is not supported when writing bool val.",
+                    (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
             return false;
     }
 }
@@ -185,8 +147,8 @@
             writeUtf8StringImpl(id, val.c_str(), val.size());
             return true;
         default:
-            ALOGW("Field type %d is not supported when writing string val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            ALOGW("Field type %" PRIu64 " is not supported when writing string val.",
+                    (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
             return false;
     }
 }
@@ -206,8 +168,8 @@
             writeMessageBytesImpl(id, val, size);
             return true;
         default:
-            ALOGW("Field type %d is not supported when writing char[] val.",
-                    (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+            ALOGW("Field type %" PRIu64 " is not supported when writing char[] val.",
+                    (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
             return false;
     }
 }
@@ -282,12 +244,14 @@
 {
     if (token != mExpectedObjectToken) {
         ALOGE("Unexpected token: 0x%" PRIx64 ", should be 0x%" PRIx64, token, mExpectedObjectToken);
+        mDepth = UINT32_C(-1); // make depth invalid
         return;
     }
 
     uint32_t depth = getDepthFromToken(token);
     if (depth != (mDepth & 0x01ff)) {
         ALOGE("Unexpected depth: %" PRIu32 ", should be %" PRIu32, depth, mDepth);
+        mDepth = UINT32_C(-1); // make depth invalid
         return;
     }
     mDepth--;
@@ -320,7 +284,7 @@
 ProtoOutputStream::compact() {
     if (mCompact) return true;
     if (mDepth != 0) {
-        ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing calls to end.", mDepth);
+        ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing or extra calls to end.", mDepth);
         return false;
     }
     // record the size of the original buffer.
@@ -463,7 +427,7 @@
 {
     if (!compact()) {
         ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
-        // TODO: handle this error
+        return 0;
     }
     return mBuffer.size();
 }
@@ -487,7 +451,7 @@
 {
     if (!compact()) {
         ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
-        // TODO: handle this error
+        mBuffer.clear();
     }
     return mBuffer.begin();
 }
@@ -542,17 +506,17 @@
 }
 
 inline void
-ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
+ProtoOutputStream::writeInt64Impl(uint32_t id, int64_t val)
 {
     mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
-    mBuffer.writeRawVarint64((uint64_t)val);
+    mBuffer.writeRawVarint64(val);
 }
 
 inline void
-ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
+ProtoOutputStream::writeInt32Impl(uint32_t id, int32_t val)
 {
     mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
-    mBuffer.writeRawVarint32((uint32_t)val);
+    mBuffer.writeRawVarint32(val);
 }
 
 inline void
@@ -584,28 +548,28 @@
 }
 
 inline void
-ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
+ProtoOutputStream::writeSFixed64Impl(uint32_t id, int64_t val)
 {
     mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
-    mBuffer.writeRawFixed64((uint64_t)val);
+    mBuffer.writeRawFixed64(val);
 }
 
 inline void
-ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
+ProtoOutputStream::writeSFixed32Impl(uint32_t id, int32_t val)
 {
     mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
-    mBuffer.writeRawFixed32((uint32_t)val);
+    mBuffer.writeRawFixed32(val);
 }
 
 inline void
-ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
+ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, int64_t val)
 {
     mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
     mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
 }
 
 inline void
-ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
+ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int32_t val)
 {
     mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
     mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
diff --git a/libs/protoutil/tests/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp
index 615ab4a..964fc8e 100644
--- a/libs/protoutil/tests/EncodedBuffer_test.cpp
+++ b/libs/protoutil/tests/EncodedBuffer_test.cpp
@@ -17,9 +17,138 @@
 
 using namespace android::util;
 
+constexpr size_t TEST_CHUNK_SIZE = 16UL;
+constexpr size_t TEST_CHUNK_HALF_SIZE = TEST_CHUNK_SIZE / 2;
+constexpr size_t TEST_CHUNK_3X_SIZE = 3 * TEST_CHUNK_SIZE;
+
+static void expectPointer(EncodedBuffer::Pointer* p, size_t pos) {
+    EXPECT_EQ(p->pos(), pos);
+    EXPECT_EQ(p->index(), pos / TEST_CHUNK_SIZE);
+    EXPECT_EQ(p->offset(), pos % TEST_CHUNK_SIZE);
+}
+
+TEST(EncodedBufferTest, WriteSimple) {
+    EncodedBuffer buffer(TEST_CHUNK_SIZE);
+    EXPECT_EQ(buffer.size(), 0UL);
+    expectPointer(buffer.wp(), 0);
+    EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_SIZE);
+    for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
+        buffer.writeRawByte(50 + i);
+    }
+    EXPECT_EQ(buffer.size(), TEST_CHUNK_HALF_SIZE);
+    expectPointer(buffer.wp(), TEST_CHUNK_HALF_SIZE);
+    EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+    for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
+        buffer.writeRawByte(80 + i);
+    }
+    EXPECT_EQ(buffer.size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+    expectPointer(buffer.wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+    EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+
+    // verifies the buffer's data
+    expectPointer(buffer.ep(), 0);
+    for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
+        EXPECT_EQ(buffer.readRawByte(), 50 + i);
+    }
+    for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
+        EXPECT_EQ(buffer.readRawByte(), 80 + i);
+    }
+
+    // clears the buffer
+    buffer.clear();
+    EXPECT_EQ(buffer.size(), 0UL);
+    expectPointer(buffer.wp(), 0);
+}
+
+TEST(EncodedBufferTest, WriteVarint) {
+    EncodedBuffer buffer(TEST_CHUNK_SIZE);
+    size_t expected_buffer_size = 0;
+    EXPECT_EQ(buffer.writeRawVarint32(13), 1);
+    expected_buffer_size += 1;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    EXPECT_EQ(buffer.writeRawVarint32(UINT32_C(-1)), 5);
+    expected_buffer_size += 5;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+    EXPECT_EQ(buffer.writeRawVarint64(200), 2);
+    expected_buffer_size += 2;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    EXPECT_EQ(buffer.writeRawVarint64(UINT64_C(-1)), 10);
+    expected_buffer_size += 10;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+    buffer.writeRawFixed32(UINT32_C(-1));
+    expected_buffer_size += 4;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    buffer.writeRawFixed64(UINT64_C(-1));
+    expected_buffer_size += 8;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+    EXPECT_EQ(buffer.writeHeader(32, 2), 2);
+    expected_buffer_size += 2;
+    EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+    // verify data are correctly written to the buffer.
+    expectPointer(buffer.ep(), 0);
+    EXPECT_EQ(buffer.readRawVarint(), UINT32_C(13));
+    EXPECT_EQ(buffer.readRawVarint(), UINT32_C(-1));
+    EXPECT_EQ(buffer.readRawVarint(), UINT64_C(200));
+    EXPECT_EQ(buffer.readRawVarint(), UINT64_C(-1));
+    EXPECT_EQ(buffer.readRawFixed32(), UINT32_C(-1));
+    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(-1));
+    EXPECT_EQ(buffer.readRawVarint(), UINT64_C((32 << 3) + 2));
+    expectPointer(buffer.ep(), expected_buffer_size);
+}
+
+TEST(EncodedBufferTest, Edit) {
+    EncodedBuffer buffer(TEST_CHUNK_SIZE);
+    buffer.writeRawFixed64(0xdeadbeefdeadbeef);
+    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
+
+    buffer.editRawFixed32(4, 0x12345678);
+    // fixed 64 is little endian order.
+    buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
+    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678deadbeef));
+
+    buffer.wp()->rewind();
+    expectPointer(buffer.wp(), 0);
+    buffer.copy(4, 3);
+    buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
+    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678de345678));
+}
+
+TEST(EncodedBufferTest, ReadSimple) {
+    EncodedBuffer buffer(TEST_CHUNK_SIZE);
+    for (size_t i = 0; i < TEST_CHUNK_3X_SIZE; i++) {
+        buffer.writeRawByte(i);
+    }
+    auto iter = buffer.begin();
+    EXPECT_EQ(iter.size(), TEST_CHUNK_3X_SIZE);
+    EXPECT_EQ(iter.bytesRead(), 0);
+
+    expectPointer(iter.rp(), 0);
+    while (iter.readBuffer() != NULL) {
+        iter.rp()->move(iter.currentToRead());
+    }
+    EXPECT_EQ(iter.bytesRead(), TEST_CHUNK_3X_SIZE);
+    expectPointer(iter.rp(), TEST_CHUNK_3X_SIZE);
+
+    iter.rp()->rewind();
+    expectPointer(iter.rp(), 0);
+    uint8_t val = 0;
+    while (iter.hasNext()) {
+        EXPECT_EQ(iter.next(), val);
+        val++;
+    }
+    EXPECT_EQ(iter.bytesRead(), TEST_CHUNK_3X_SIZE);
+    expectPointer(iter.rp(), TEST_CHUNK_3X_SIZE);
+}
+
 TEST(EncodedBufferTest, ReadVarint) {
     EncodedBuffer buffer;
     uint64_t val = UINT64_C(1522865904593);
-    buffer.writeRawVarint64(val);
-    EXPECT_EQ(val, buffer.begin().readRawVarint());
+    size_t len = buffer.writeRawVarint64(val);
+    auto iter = buffer.begin();
+    EXPECT_EQ(iter.size(), len);
+    EXPECT_EQ(iter.readRawVarint(), val);
 }
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
new file mode 100644
index 0000000..27ee13d
--- /dev/null
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -0,0 +1,228 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <android/util/protobuf.h>
+#include <android/util/ProtoOutputStream.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "frameworks/base/libs/protoutil/tests/test.pb.h"
+
+using namespace android::base;
+using namespace android::util;
+using ::testing::StrEq;
+
+static std::string flushToString(ProtoOutputStream* proto) {
+    TemporaryFile tf;
+    std::string content;
+
+    EXPECT_NE(tf.fd, -1);
+    EXPECT_TRUE(proto->flush(tf.fd));
+    EXPECT_TRUE(ReadFileToString(tf.path, &content));
+    return content;
+}
+
+static std::string iterateToString(ProtoOutputStream* proto) {
+    std::string content;
+    content.reserve(proto->size());
+    auto iter = proto->data();
+    while (iter.hasNext()) {
+        content.push_back(iter.next());
+    }
+    return content;
+}
+
+TEST(ProtoOutputStreamTest, Primitives) {
+    std::string s = "hello";
+    const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
+
+    ProtoOutputStream proto;
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
+
+    PrimitiveProto primitives;
+    ASSERT_TRUE(primitives.ParseFromString(flushToString(&proto)));
+    EXPECT_EQ(primitives.val_int32(), 123);
+    EXPECT_EQ(primitives.val_int64(), -1);
+    EXPECT_EQ(primitives.val_float(), -23.5f);
+    EXPECT_EQ(primitives.val_double(), 324.5f);
+    EXPECT_EQ(primitives.val_uint32(), 3424);
+    EXPECT_EQ(primitives.val_uint64(), 57);
+    EXPECT_EQ(primitives.val_fixed32(), -20);
+    EXPECT_EQ(primitives.val_fixed64(), -37);
+    EXPECT_EQ(primitives.val_bool(), true);
+    EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
+    EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
+    EXPECT_EQ(primitives.val_sfixed32(), 63);
+    EXPECT_EQ(primitives.val_sfixed64(), -54);
+    EXPECT_EQ(primitives.val_sint32(), -533);
+    EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
+    EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
+}
+
+TEST(ProtoOutputStreamTest, Complex) {
+    std::string name1 = "cat";
+    std::string name2 = "dog";
+    const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' };
+    const char data2[4] = { 'f', 'o', 'o', 'd' };
+
+    ProtoOutputStream proto;
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72));
+    uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1));
+    // specify the length to test the write(id, bytes, length) function.
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5));
+    proto.end(token1);
+    uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4));
+    proto.end(token2);
+
+    ComplexProto complex;
+    ASSERT_TRUE(complex.ParseFromString(iterateToString(&proto)));
+    EXPECT_EQ(complex.ints_size(), 3);
+    EXPECT_EQ(complex.ints(0), 23);
+    EXPECT_EQ(complex.ints(1), 101);
+    EXPECT_EQ(complex.ints(2), -72);
+    EXPECT_EQ(complex.logs_size(), 2);
+    ComplexProto::Log log1 = complex.logs(0);
+    EXPECT_EQ(log1.id(), 12);
+    EXPECT_THAT(log1.name(), StrEq(name1.c_str()));
+    EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!'
+    ComplexProto::Log log2 = complex.logs(1);
+    EXPECT_EQ(log2.id(), 98);
+    EXPECT_THAT(log2.name(), StrEq(name2.c_str()));
+    EXPECT_THAT(log2.data(), StrEq("food"));
+}
+
+TEST(ProtoOutputStreamTest, Reusability) {
+    ProtoOutputStream proto;
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 15));
+    EXPECT_EQ(proto.bytesWritten(), 4);
+    EXPECT_EQ(proto.size(), 4);
+    // Can't write to proto after compact
+    EXPECT_FALSE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 94));
+
+    ComplexProto beforeClear;
+    ASSERT_TRUE(beforeClear.ParseFromString(flushToString(&proto)));
+    EXPECT_EQ(beforeClear.ints_size(), 2);
+    EXPECT_EQ(beforeClear.ints(0), 32);
+    EXPECT_EQ(beforeClear.ints(1), 15);
+
+    proto.clear();
+    EXPECT_EQ(proto.bytesWritten(), 0);
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 1076));
+
+    ComplexProto afterClear;
+    ASSERT_TRUE(afterClear.ParseFromString(flushToString(&proto)));
+    EXPECT_EQ(afterClear.ints_size(), 1);
+    EXPECT_EQ(afterClear.ints(0), 1076);
+}
+
+TEST(ProtoOutputStreamTest, AdvancedEncoding) {
+    ProtoOutputStream proto;
+    proto.writeRawVarint((ComplexProto::kIntsFieldNumber << FIELD_ID_SHIFT) + WIRE_TYPE_VARINT);
+    proto.writeRawVarint(UINT64_C(-123809234));
+    proto.writeLengthDelimitedHeader(ComplexProto::kLogsFieldNumber, 8);
+    proto.writeRawByte((ComplexProto::Log::kDataFieldNumber << FIELD_ID_SHIFT) + WIRE_TYPE_LENGTH_DELIMITED);
+    proto.writeRawByte(6);
+    proto.writeRawByte('b');
+    proto.writeRawByte('a');
+    proto.writeRawByte('n');
+    proto.writeRawByte('a');
+    proto.writeRawByte('n');
+    proto.writeRawByte('a');
+    uint64_t token = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 14);
+    proto.end(token);
+
+    ComplexProto complex;
+    ASSERT_TRUE(complex.ParseFromString(flushToString(&proto)));
+    EXPECT_EQ(complex.ints_size(), 1);
+    EXPECT_EQ(complex.ints(0), UINT64_C(-123809234));
+    EXPECT_EQ(complex.logs_size(), 2);
+    ComplexProto::Log log1 = complex.logs(0);
+    EXPECT_FALSE(log1.has_id());
+    EXPECT_FALSE(log1.has_name());
+    EXPECT_THAT(log1.data(), StrEq("banana"));
+    ComplexProto::Log log2 = complex.logs(1);
+    EXPECT_EQ(log2.id(), 14);
+    EXPECT_FALSE(log2.has_name());
+    EXPECT_FALSE(log2.has_data());
+}
+
+TEST(ProtoOutputStreamTest, InvalidTypes) {
+    ProtoOutputStream proto;
+    EXPECT_FALSE(proto.write(FIELD_TYPE_UNKNOWN | PrimitiveProto::kValInt32FieldNumber, 790));
+    EXPECT_FALSE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 234.34));
+    EXPECT_FALSE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, 18.73f));
+    EXPECT_EQ(proto.size(), 0);
+}
+
+TEST(ProtoOutputStreamTest, NoEndCalled) {
+    ProtoOutputStream proto;
+    proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+    // no proto.end called
+    EXPECT_NE(proto.bytesWritten(), 0);
+    EXPECT_EQ(proto.size(), 0);
+    EXPECT_EQ(proto.data().size(), 0);
+    EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
+
+
+TEST(ProtoOutputStreamTest, TwoEndCalled) {
+    ProtoOutputStream proto;
+    uint64_t token = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+    proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+    proto.end(token);
+    proto.end(token);
+    EXPECT_NE(proto.bytesWritten(), 0);
+    EXPECT_EQ(proto.size(), 0);
+    EXPECT_EQ(proto.data().size(), 0);
+    EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
+
+TEST(ProtoOutputStreamTest, NoStartCalled) {
+    ProtoOutputStream proto;
+    uint64_t wrongToken = UINT64_C(324536345);
+    // no proto.start called
+    proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+    proto.end(wrongToken);
+    EXPECT_NE(proto.bytesWritten(), 0);
+    EXPECT_EQ(proto.size(), 0);
+    EXPECT_EQ(proto.data().size(), 0);
+    EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
diff --git a/libs/protoutil/tests/protobuf_test.cpp b/libs/protoutil/tests/protobuf_test.cpp
new file mode 100644
index 0000000..5ca3e64
--- /dev/null
+++ b/libs/protoutil/tests/protobuf_test.cpp
@@ -0,0 +1,51 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <android/util/protobuf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace android::util;
+
+TEST(ProtobufTest, All) {
+    EXPECT_EQ(read_wire_type(UINT32_C(17)), 1);
+    EXPECT_EQ(read_field_id(UINT32_C(17)), 2);
+    EXPECT_EQ(get_varint_size(UINT64_C(234134)), 3);
+    EXPECT_EQ(get_varint_size(UINT64_C(-1)), 10);
+
+    constexpr uint8_t UNSET_BYTE = 0xAB;
+
+    uint8_t buf[11];
+    memset(buf, UNSET_BYTE, sizeof(buf));
+    EXPECT_EQ(write_raw_varint(buf, UINT64_C(150)) - buf, 2);
+    EXPECT_EQ(buf[0], 0x96);
+    EXPECT_EQ(buf[1], 0x01);
+    EXPECT_EQ(buf[2], UNSET_BYTE);
+
+    memset(buf, UNSET_BYTE, sizeof(buf));
+    EXPECT_EQ(write_raw_varint(buf, UINT64_C(-2)) - buf, 10);
+    EXPECT_EQ(buf[0], 0xfe);
+    for (int i = 1; i < 9; i++) {
+        EXPECT_EQ(buf[i], 0xff);
+    }
+    EXPECT_EQ(buf[9], 0x01);
+    EXPECT_EQ(buf[10], UNSET_BYTE);
+
+    uint8_t header[20];
+    memset(header, UNSET_BYTE, sizeof(header));
+    EXPECT_EQ(write_length_delimited_tag_header(header, 3, 150) - header, 3);
+    EXPECT_EQ(header[0], 26);
+    EXPECT_EQ(header[1], 0x96);
+    EXPECT_EQ(header[2], 0x01);
+    EXPECT_EQ(header[3], UNSET_BYTE);
+}
\ No newline at end of file
diff --git a/libs/protoutil/tests/test.proto b/libs/protoutil/tests/test.proto
new file mode 100644
index 0000000..52c55f3
--- /dev/null
+++ b/libs/protoutil/tests/test.proto
@@ -0,0 +1,42 @@
+// This proto file is only used for testing purpose.
+syntax = "proto2";
+
+package android.util;
+
+message PrimitiveProto {
+
+    optional int32 val_int32 = 1;
+    optional int64 val_int64 = 2;
+    optional float val_float = 3;
+    optional double val_double = 4;
+    optional uint32 val_uint32 = 5;
+    optional uint64 val_uint64 = 6;
+    optional fixed32 val_fixed32 = 7;
+    optional fixed64 val_fixed64 = 8;
+    optional bool val_bool = 9;
+    optional string val_string = 10;
+    optional bytes val_bytes = 11;
+    optional sfixed32 val_sfixed32 = 12;
+    optional sfixed64 val_sfixed64 = 13;
+    optional sint32 val_sint32 = 14;
+    optional sint64 val_sint64 = 15;
+
+    enum Count {
+        ZERO = 0;
+        ONE = 1;
+        TWO = 2;
+    };
+    optional Count val_enum = 16;
+}
+
+message ComplexProto {
+
+    repeated int32 ints = 1;
+
+    message Log {
+        optional int32 id = 1;
+        optional string name = 2;
+        optional bytes data = 3;
+    }
+    repeated Log logs = 2;
+}
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index c9d2f7f..703f2dc 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -17,6 +17,7 @@
 package android.location;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -999,6 +1000,7 @@
      * @see #isComplete
      * @hide
      */
+    @TestApi
     @SystemApi
     public void makeComplete() {
         if (mProvider == null) mProvider = "?";
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 7ac1529..3a0a58e 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -86,7 +86,7 @@
  * <p>
  * Once the app has a sessionId, it can construct a MediaCrypto object from the UUID and
  * sessionId.  The MediaCrypto object is registered with the MediaCodec in the
- * {@link MediaCodec.#configure} method to enable the codec to decrypt content.
+ * {@link MediaCodec#configure} method to enable the codec to decrypt content.
  * <p>
  * When the app has constructed {@link android.media.MediaExtractor},
  * {@link android.media.MediaCodec} and {@link android.media.MediaCrypto} objects,
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index f476a6c..8a757b8 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -488,6 +488,7 @@
         private int mCompilation;
         private boolean mIsDrm;
         private boolean mNoMedia;   // flag to suppress file from appearing in media tables
+        private boolean mScanSuccess;
         private int mWidth;
         private int mHeight;
 
@@ -502,6 +503,7 @@
             mFileType = 0;
             mFileSize = fileSize;
             mIsDrm = false;
+            mScanSuccess = true;
 
             if (!isDirectory) {
                 if (!noMedia && isNoMediaFile(path)) {
@@ -623,14 +625,6 @@
                     if (noMedia) {
                         result = endFile(entry, false, false, false, false, false);
                     } else {
-                        String lowpath = path.toLowerCase(Locale.ROOT);
-                        boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0);
-                        boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
-                        boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0);
-                        boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0);
-                        boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
-                            (!ringtones && !notifications && !alarms && !podcasts);
-
                         boolean isaudio = MediaFile.isAudioFileType(mFileType);
                         boolean isvideo = MediaFile.isVideoFileType(mFileType);
                         boolean isimage = MediaFile.isImageFileType(mFileType);
@@ -642,13 +636,22 @@
 
                         // we only extract metadata for audio and video files
                         if (isaudio || isvideo) {
-                            processFile(path, mimeType, this);
+                            mScanSuccess = processFile(path, mimeType, this);
                         }
 
                         if (isimage) {
-                            processImageFile(path);
+                            mScanSuccess = processImageFile(path);
                         }
 
+                        String lowpath = path.toLowerCase(Locale.ROOT);
+                        boolean ringtones = mScanSuccess && (lowpath.indexOf(RINGTONES_DIR) > 0);
+                        boolean notifications = mScanSuccess &&
+                                (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
+                        boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
+                        boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCAST_DIR) > 0);
+                        boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
+                            (!ringtones && !notifications && !alarms && !podcasts));
+
                         result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
                     }
                 }
@@ -816,16 +819,18 @@
             return genreTagValue;
         }
 
-        private void processImageFile(String path) {
+        private boolean processImageFile(String path) {
             try {
                 mBitmapOptions.outWidth = 0;
                 mBitmapOptions.outHeight = 0;
                 BitmapFactory.decodeFile(path, mBitmapOptions);
                 mWidth = mBitmapOptions.outWidth;
                 mHeight = mBitmapOptions.outHeight;
+                return mWidth > 0 && mHeight > 0;
             } catch (Throwable th) {
                 // ignore;
             }
+            return false;
         }
 
         public void setMimeType(String mimeType) {
@@ -878,7 +883,7 @@
                     }
                 } else if (MediaFile.isImageFileType(mFileType)) {
                     // FIXME - add DESCRIPTION
-                } else if (MediaFile.isAudioFileType(mFileType)) {
+                } else if (mScanSuccess && MediaFile.isAudioFileType(mFileType)) {
                     map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
                             mArtist : MediaStore.UNKNOWN_STRING);
                     map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
@@ -894,6 +899,10 @@
                     map.put(Audio.Media.DURATION, mDuration);
                     map.put(Audio.Media.COMPILATION, mCompilation);
                 }
+                if (!mScanSuccess) {
+                    // force mediaprovider to not determine the media type from the mime type
+                    map.put(Files.FileColumns.MEDIA_TYPE, 0);
+                }
             }
             return map;
         }
@@ -1001,7 +1010,7 @@
 
             Uri tableUri = mFilesUri;
             MediaInserter inserter = mMediaInserter;
-            if (!mNoMedia) {
+            if (mScanSuccess && !mNoMedia) {
                 if (MediaFile.isVideoFileType(mFileType)) {
                     tableUri = mVideoUri;
                 } else if (MediaFile.isImageFileType(mFileType)) {
@@ -1071,7 +1080,7 @@
                 values.remove(MediaStore.MediaColumns.DATA);
 
                 int mediaType = 0;
-                if (!MediaScanner.isNoMediaPath(entry.mPath)) {
+                if (mScanSuccess && !MediaScanner.isNoMediaPath(entry.mPath)) {
                     int fileType = MediaFile.getFileTypeForMimeType(mMimeType);
                     if (MediaFile.isAudioFileType(fileType)) {
                         mediaType = FileColumns.MEDIA_TYPE_AUDIO;
@@ -1890,7 +1899,7 @@
     }
 
     private native void processDirectory(String path, MediaScannerClient client);
-    private native void processFile(String path, String mimeType, MediaScannerClient client);
+    private native boolean processFile(String path, String mimeType, MediaScannerClient client);
     private native void setLocale(String locale);
 
     public native byte[] extractAlbumArt(FileDescriptor fd);
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 3b475b2..c0ceb01 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -264,7 +264,7 @@
     env->ReleaseStringUTFChars(path, pathStr);
 }
 
-static void
+static jboolean
 android_media_MediaScanner_processFile(
         JNIEnv *env, jobject thiz, jstring path,
         jstring mimeType, jobject client)
@@ -275,17 +275,17 @@
     MediaScanner *mp = getNativeScanner_l(env, thiz);
     if (mp == NULL) {
         jniThrowException(env, kRunTimeException, "No scanner available");
-        return;
+        return false;
     }
 
     if (path == NULL) {
         jniThrowException(env, kIllegalArgumentException, NULL);
-        return;
+        return false;
     }
 
     const char *pathStr = env->GetStringUTFChars(path, NULL);
     if (pathStr == NULL) {  // Out of memory
-        return;
+        return false;
     }
 
     const char *mimeTypeStr =
@@ -293,7 +293,7 @@
     if (mimeType && mimeTypeStr == NULL) {  // Out of memory
         // ReleaseStringUTFChars can be called with an exception pending.
         env->ReleaseStringUTFChars(path, pathStr);
-        return;
+        return false;
     }
 
     MyMediaScannerClient myClient(env, client);
@@ -305,6 +305,7 @@
     if (mimeType) {
         env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
     }
+    return result != MEDIA_SCAN_RESULT_ERROR;
 }
 
 static void
@@ -421,7 +422,7 @@
 
     {
         "processFile",
-        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
+        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)Z",
         (void *)android_media_MediaScanner_processFile
     },
 
diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp
new file mode 100644
index 0000000..cbacd48
--- /dev/null
+++ b/nfc-extras/Android.bp
@@ -0,0 +1,21 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_sdk_library {
+    name: "com.android.nfc_extras",
+    srcs: ["java/**/*.java"],
+    api_packages: ["com.android.nfc_extras"],
+}
diff --git a/nfc-extras/Android.mk b/nfc-extras/Android.mk
deleted file mode 100644
index 03de00c..0000000
--- a/nfc-extras/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.nfc_extras
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under,java)
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.nfc_extras-stubs-gen
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := $(call all-java-files-under,java)
-# This is to reference SdkConstant annotation; not part of this lib.
-LOCAL_DROIDDOC_SOURCE_PATH := frameworks/base/core/java/android/annotation
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/com.android.nfc_extras.stubs_intermediates/src
-LOCAL_DROIDDOC_OPTIONS:= \
-    -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-    -stubpackages com.android.nfc_extras \
-    -nodocs
-LOCAL_UNINSTALLABLE_MODULE := true
-include $(BUILD_DROIDDOC)
-com_android_nfc_extras_gen_stamp := $(full_target)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := com.android.nfc_extras.stubs
-# This is to reference SdkConstant annotation; not part of this lib.
-LOCAL_SRC_FILES := ../core/java/android/annotation/SdkConstant.java
-LOCAL_SDK_VERSION := current
-LOCAL_ADDITIONAL_DEPENDENCIES := $(com_android_nfc_extras_gen_stamp)
-com_android_nfc_extras_gen_stamp :=
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/nfc-extras/com.android.nfc_extras.xml b/nfc-extras/com.android.nfc_extras.xml
deleted file mode 100644
index 370145d..0000000
--- a/nfc-extras/com.android.nfc_extras.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<permissions>
-    <library name="com.android.nfc_extras"
-            file="/system/framework/com.android.nfc_extras.jar" />
-</permissions>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 7479d9a..b1933373 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -111,13 +111,11 @@
         mWebView.setWebViewClient(mWebViewClient);
         mWebView.setWebChromeClient(new MyWebChromeClient());
 
-        mNetwork = getNetworkForCaptivePortal();
-        if (mNetwork == null) {
+        final Network network = getNetworkForCaptivePortal();
+        if (network == null) {
             requestNetworkForCaptivePortal();
         } else {
-            mCm.bindProcessToNetwork(mNetwork);
-            mCm.setProcessDefaultNetworkForHostResolution(
-                    ResolvUtil.getNetworkWithUseLocalNameserversFlag(mNetwork));
+            setNetwork(network);
             // Start initial page load so WebView finishes loading proxy settings.
             // Actual load of mUrl is initiated by MyWebViewClient.
             mWebView.loadData("", "text/html", null);
@@ -159,6 +157,15 @@
         super.onDestroy();
     }
 
+    private void setNetwork(Network network) {
+        if (network != null) {
+            mCm.bindProcessToNetwork(network);
+            mCm.setProcessDefaultNetworkForHostResolution(
+                    ResolvUtil.getNetworkWithUseLocalNameserversFlag(network));
+        }
+        mNetwork = network;
+    }
+
     // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
     private void setWebViewProxy() {
         LoadedApk loadedApk = getApplication().mLoadedApk;
@@ -235,6 +242,7 @@
     private void testForCaptivePortal() {
         mTestingThread = new Thread(new Runnable() {
             public void run() {
+                final Network network = ResolvUtil.makeNetworkWithPrivateDnsBypass(mNetwork);
                 // Give time for captive portal to open.
                 try {
                     Thread.sleep(1000);
@@ -245,7 +253,7 @@
                 int httpResponseCode = 500;
                 int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
                 try {
-                    urlConnection = (HttpURLConnection) mNetwork.openConnection(
+                    urlConnection = (HttpURLConnection) network.openConnection(
                             new URL(mCm.getCaptivePortalServerUrl()));
                     urlConnection.setInstanceFollowRedirects(false);
                     urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
@@ -292,8 +300,7 @@
             @Override
             public void onAvailable(Network network) {
                 if (DBG) logd("Network available: " + network);
-                mCm.bindProcessToNetwork(network);
-                mNetwork = network;
+                setNetwork(network);
                 runOnUiThreadIfNotFinishing(() -> {
                     if (mReload) {
                         mWebView.reload();
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/PrinterHashMap.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/PrinterHashMap.java
deleted file mode 100644
index 61956f6..0000000
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/PrinterHashMap.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printservice.recommendation.plugin.hp;
-
-import android.net.nsd.NsdServiceInfo;
-
-import java.util.HashMap;
-
-final class PrinterHashMap extends HashMap<String, NsdServiceInfo> {
-    public static String getKey(NsdServiceInfo serviceInfo) {
-        return serviceInfo.getServiceName();
-    }
-    public NsdServiceInfo addPrinter(NsdServiceInfo device) {
-        return put(getKey(device), device);
-    }
-    public NsdServiceInfo removePrinter(NsdServiceInfo device) {
-        return remove(getKey(device));
-    }
-}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java
index 600af1f..9535ef0 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/hp/ServiceListener.java
@@ -24,6 +24,7 @@
 
 import com.android.printservice.recommendation.R;
 import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
+import com.android.printservice.recommendation.util.PrinterHashMap;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -183,9 +184,7 @@
         ArrayList<InetAddress> printerAddressess = new ArrayList<>();
 
         for (PrinterHashMap oneVendorPrinters : mVendorHashMap.values()) {
-            for (NsdServiceInfo printer : oneVendorPrinters.values()) {
-                printerAddressess.add(printer.getHost());
-            }
+            printerAddressess.addAll(oneVendorPrinters.getPrinterAddresses());
         }
 
         return printerAddressess;
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/PrinterHashMap.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/PrinterHashMap.java
deleted file mode 100755
index b88c7c7..0000000
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/PrinterHashMap.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.printservice.recommendation.plugin.xerox;
-
-import android.net.nsd.NsdServiceInfo;
-
-import java.util.HashMap;
-
-final class PrinterHashMap extends HashMap<String, NsdServiceInfo> {
-    public static String getKey(NsdServiceInfo serviceInfo) {
-        return serviceInfo.getServiceName();
-    }
-
-    public NsdServiceInfo addPrinter(NsdServiceInfo device) {
-        return put(getKey(device), device);
-    }
-
-    public NsdServiceInfo removePrinter(NsdServiceInfo device) {
-        return remove(getKey(device));
-    }
-}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java
index 4d0efd8..9ada969 100755
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java
@@ -22,6 +22,7 @@
 
 import com.android.printservice.recommendation.util.DiscoveryListenerMultiplexer;
 import com.android.printservice.recommendation.util.NsdResolveQueue;
+import com.android.printservice.recommendation.util.PrinterHashMap;
 
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -195,12 +196,7 @@
     }
 
     public ArrayList<InetAddress> getPrinters() {
-        ArrayList<InetAddress> printerAddresses = new ArrayList<>();
-        for (NsdServiceInfo printer : mPrinterHashMap.values()) {
-            printerAddresses.add(printer.getHost());
-        }
-
-        return printerAddresses;
+        return mPrinterHashMap.getPrinterAddresses();
     }
 
 }
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/MDNSFilteredDiscovery.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/MDNSFilteredDiscovery.java
index c08ca6e..65cef94 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/MDNSFilteredDiscovery.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/MDNSFilteredDiscovery.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.net.nsd.NsdManager;
 import android.net.nsd.NsdServiceInfo;
+import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.GuardedBy;
@@ -27,9 +28,8 @@
 
 import com.android.printservice.recommendation.PrintServicePlugin;
 
-import java.net.InetAddress;
-import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -55,9 +55,9 @@
         boolean matchesCriteria(NsdServiceInfo nsdServiceInfo);
     }
 
-    /** Printer identifiers of the mPrinters found. */
+    /** Printers found. */
     @GuardedBy("mLock")
-    private final @NonNull HashSet<InetAddress> mPrinters;
+    private final @NonNull PrinterHashMap mPrinters;
 
     /** Service types discovered by this plugin */
     private final @NonNull HashSet<String> mServiceTypes;
@@ -97,7 +97,7 @@
         mPrinterFilter = Preconditions.checkNotNull(printerFilter, "printerFilter");
 
         mResolveQueue = NsdResolveQueue.getInstance();
-        mPrinters = new HashSet<>();
+        mPrinters = new PrinterHashMap();
     }
 
     /**
@@ -107,6 +107,12 @@
         return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
     }
 
+    private void onChanged() {
+        if (mCallback != null) {
+            mCallback.onChanged(mPrinters.getPrinterAddresses());
+        }
+    }
+
     /**
      * Start the discovery.
      *
@@ -114,7 +120,8 @@
      */
     public void start(@NonNull PrintServicePlugin.PrinterDiscoveryCallback callback) {
         mCallback = callback;
-        mCallback.onChanged(new ArrayList<>(mPrinters));
+
+        onChanged();
 
         for (String serviceType : mServiceTypes) {
             DiscoveryListenerMultiplexer.addListener(getNDSManager(), serviceType, this);
@@ -167,11 +174,12 @@
 
                     @Override
                     public void onServiceResolved(NsdServiceInfo serviceInfo) {
-                        if (mPrinterFilter.matchesCriteria(serviceInfo)) {
+                        if (!TextUtils.isEmpty(PrinterHashMap.getKey(serviceInfo))
+                                && mPrinterFilter.matchesCriteria(serviceInfo)) {
                             if (mCallback != null) {
-                                boolean added = mPrinters.add(serviceInfo.getHost());
-                                if (added) {
-                                    mCallback.onChanged(new ArrayList<>(mPrinters));
+                                NsdServiceInfo old = mPrinters.addPrinter(serviceInfo);
+                                if (!Objects.equals(old, serviceInfo)) {
+                                    onChanged();
                                 }
                             }
                         }
@@ -181,26 +189,9 @@
 
     @Override
     public void onServiceLost(NsdServiceInfo serviceInfo) {
-        mResolveQueue.resolve(getNDSManager(), serviceInfo,
-                new NsdManager.ResolveListener() {
-                    @Override
-                    public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
-                        Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
-                                + errorCode);
-                    }
-
-                    @Override
-                    public void onServiceResolved(NsdServiceInfo serviceInfo) {
-                        if (mPrinterFilter.matchesCriteria(serviceInfo)) {
-                            if (mCallback != null) {
-                                boolean removed = mPrinters.remove(serviceInfo.getHost());
-
-                                if (removed) {
-                                    mCallback.onChanged(new ArrayList<>(mPrinters));
-                                }
-                            }
-                        }
-                    }
-                });
+        NsdServiceInfo oldAddress = mPrinters.removePrinter(serviceInfo);
+        if (oldAddress != null) {
+            onChanged();
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/PrinterHashMap.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/PrinterHashMap.java
new file mode 100644
index 0000000..ee35edb
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/util/PrinterHashMap.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation.util;
+
+import android.net.nsd.NsdServiceInfo;
+import android.util.ArrayMap;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+
+/**
+ * Map to store {@link NsdServiceInfo} belonging to printers. If two infos have the same
+ * {@link PrinterHashMap#getKey(NsdServiceInfo) key} they are considered the same.
+ */
+public class PrinterHashMap {
+    private final ArrayMap<String, NsdServiceInfo> mPrinters = new ArrayMap<>();
+
+    /**
+     * Key uniquely identifying a printer.
+     *
+     * @param serviceInfo The service info describing the printer
+     *
+     * @return The key
+     */
+    public static String getKey(NsdServiceInfo serviceInfo) {
+        return serviceInfo.getServiceName();
+    }
+
+    /**
+     * Add a printer.
+     *
+     * @param device The service info of the printer
+     *
+     * @return The service info of the printer that was previously registered for the same key
+     */
+    public NsdServiceInfo addPrinter(NsdServiceInfo device) {
+        return mPrinters.put(getKey(device), device);
+    }
+
+    /**
+     * Remove a printer.
+     *
+     * @param device The service info of the printer
+     *
+     * @return The service info of the printer that was previously registered for the same key
+     */
+    public NsdServiceInfo removePrinter(NsdServiceInfo device) {
+        return mPrinters.remove(getKey(device));
+    }
+
+    /**
+     * @return the addresses of printers
+     */
+    public ArrayList<InetAddress> getPrinterAddresses() {
+        int numPrinters = mPrinters.size();
+        ArrayList<InetAddress> printerAddressess = new ArrayList<>(numPrinters);
+        for (int i = 0; i < numPrinters; i++) {
+            printerAddressess.add(mPrinters.valueAt(i).getHost());
+        }
+
+        return printerAddressess;
+    }
+
+    /**
+     * Remove all printers
+     */
+    public void clear() {
+        mPrinters.clear();
+    }
+
+    /**
+     * @return {@code} true iff the map is empty
+     */
+    public boolean isEmpty() {
+        return mPrinters.isEmpty();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index c756fdb..b4b5b58 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,6 +15,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.location.LocationManager;
+import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.os.BatteryManager;
 import android.os.SystemProperties;
@@ -26,7 +27,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.UserIconDrawable;
-import com.android.settingslib.wrapper.LocationManagerWrapper;
+
 import java.text.NumberFormat;
 
 public class Utils {
@@ -68,8 +69,7 @@
                 intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
         LocationManager locationManager =
                 (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
-        LocationManagerWrapper wrapper = new LocationManagerWrapper(locationManager);
-        wrapper.setLocationEnabledForUser(enabled, UserHandle.of(userId));
+        locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId));
     }
 
     public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId,
@@ -373,4 +373,15 @@
                 isDefaultOn ? 1 : 0)
                 != 0;
     }
+
+    /**
+     * get that {@link AudioManager#getMode()} is in ringing/call/communication(VoIP) status.
+     */
+    public static boolean isAudioModeOngoingCall(Context context) {
+        final AudioManager audioManager = context.getSystemService(AudioManager.class);
+        final int audioMode = audioManager.getMode();
+        return audioMode == AudioManager.MODE_RINGTONE
+                || audioMode == AudioManager.MODE_IN_CALL
+                || audioMode == AudioManager.MODE_IN_COMMUNICATION;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index afc08f4..3102239 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -29,7 +29,6 @@
 import android.util.IconDrawableFactory;
 
 import com.android.settingslib.widget.CandidateInfo;
-import com.android.settingslib.wrapper.PackageManagerWrapper;
 
 /**
  * Data model representing an app in DefaultAppPicker UI.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 6b99024..6272954 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -20,7 +20,6 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothCodecConfig;
-import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
@@ -28,9 +27,7 @@
 import android.os.ParcelUuid;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.R;
-import com.android.settingslib.wrapper.BluetoothA2dpWrapper;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -43,7 +40,6 @@
     private Context mContext;
 
     private BluetoothA2dp mService;
-    private BluetoothA2dpWrapper mServiceWrapper;
     private boolean mIsProfileReady;
 
     private final LocalBluetoothAdapter mLocalAdapter;
@@ -67,7 +63,6 @@
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
             if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothA2dp) proxy;
-            mServiceWrapper = new BluetoothA2dpWrapper(mService);
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
             while (!deviceList.isEmpty()) {
@@ -105,11 +100,6 @@
                 BluetoothProfile.A2DP);
     }
 
-    @VisibleForTesting
-    void setBluetoothA2dpWrapper(BluetoothA2dpWrapper wrapper) {
-        mServiceWrapper = wrapper;
-    }
-
     public boolean isConnectable() {
         return true;
     }
@@ -203,12 +193,12 @@
     }
 
     public boolean supportsHighQualityAudio(BluetoothDevice device) {
-        int support = mServiceWrapper.supportsOptionalCodecs(device);
+        int support = mService.supportsOptionalCodecs(device);
         return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED;
     }
 
     public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
-        int enabled = mServiceWrapper.getOptionalCodecsEnabled(device);
+        int enabled = mService.getOptionalCodecsEnabled(device);
         if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) {
             return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED;
         } else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED &&
@@ -219,8 +209,8 @@
             return true;
         }
         BluetoothCodecConfig codecConfig = null;
-        if (mServiceWrapper.getCodecStatus(device) != null) {
-            codecConfig = mServiceWrapper.getCodecStatus(device).getCodecConfig();
+        if (mService.getCodecStatus(device) != null) {
+            codecConfig = mService.getCodecStatus(device).getCodecConfig();
         }
         if (codecConfig != null)  {
             return !codecConfig.isMandatoryCodec();
@@ -233,7 +223,7 @@
         int prefValue = enabled
                 ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED
                 : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED;
-        mServiceWrapper.setOptionalCodecsEnabled(device, prefValue);
+        mService.setOptionalCodecsEnabled(device, prefValue);
         if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
             return;
         }
@@ -253,8 +243,8 @@
         // We want to get the highest priority codec, since that's the one that will be used with
         // this device, and see if it is high-quality (ie non-mandatory).
         BluetoothCodecConfig[] selectable = null;
-        if (mServiceWrapper.getCodecStatus(device) != null) {
-            selectable = mServiceWrapper.getCodecStatus(device).getCodecsSelectableCapabilities();
+        if (mService.getCodecStatus(device) != null) {
+            selectable = mService.getCodecStatus(device).getCodecsSelectableCapabilities();
             // To get the highest priority, we sort in reverse.
             Arrays.sort(selectable,
                     (a, b) -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index fb268ab..e7d7ab3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -490,6 +490,7 @@
     }
 
     private void dispatchAudioModeChanged() {
+        mDeviceManager.dispatchAudioModeChanged();
         synchronized (mCallbacks) {
             for (BluetoothCallback callback : mCallbacks) {
                 callback.onAudioModeChanged();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 7d0587a..95e443c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -23,6 +23,7 @@
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.media.AudioManager;
 import android.os.ParcelUuid;
 import android.os.SystemClock;
 import android.text.TextUtils;
@@ -51,6 +52,7 @@
     private final Context mContext;
     private final LocalBluetoothAdapter mLocalAdapter;
     private final LocalBluetoothProfileManager mProfileManager;
+    private final AudioManager mAudioManager;
     private final BluetoothDevice mDevice;
     //TODO: consider remove, BluetoothDevice.getName() is already cached
     private String mName;
@@ -123,7 +125,6 @@
     private boolean mIsActiveDeviceA2dp = false;
     private boolean mIsActiveDeviceHeadset = false;
     private boolean mIsActiveDeviceHearingAid = false;
-
     /**
      * Describes the current device and profile for logging.
      *
@@ -185,6 +186,7 @@
         mContext = context;
         mLocalAdapter = adapter;
         mProfileManager = profileManager;
+        mAudioManager = context.getSystemService(AudioManager.class);
         mDevice = device;
         mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
         fillData();
@@ -538,6 +540,12 @@
     }
 
     /**
+     * Update the profile audio state.
+     */
+    void onAudioModeChanged() {
+        dispatchAttributesChanged();
+    }
+    /**
      * Get the device status as active or non-active per Bluetooth profile.
      *
      * @param bluetoothProfile the Bluetooth profile
@@ -972,12 +980,14 @@
 
     /**
      * @return resource for string that discribes the connection state of this device.
+     * case 1: idle or playing media, show "Active" on the only one A2DP active device.
+     * case 2: in phone call, show "Active" on the only one HFP active device
      */
     public String getConnectionSummary() {
-        boolean profileConnected = false;       // at least one profile is connected
-        boolean a2dpNotConnected = false;       // A2DP is preferred but not connected
-        boolean hfpNotConnected = false;    // HFP is preferred but not connected
-        boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
+        boolean profileConnected = false;    // Updated as long as BluetoothProfile is connected
+        boolean a2dpConnected = true;        // A2DP is connected
+        boolean hfpConnected = true;         // HFP is connected
+        boolean hearingAidConnected = true;  // Hearing Aid is connected
 
         for (LocalBluetoothProfile profile : getProfiles()) {
             int connectionStatus = getProfileConnectionState(profile);
@@ -994,13 +1004,13 @@
                 case BluetoothProfile.STATE_DISCONNECTED:
                     if (profile.isProfileReady()) {
                         if ((profile instanceof A2dpProfile) ||
-                            (profile instanceof A2dpSinkProfile)){
-                            a2dpNotConnected = true;
+                                (profile instanceof A2dpSinkProfile)) {
+                            a2dpConnected = false;
                         } else if ((profile instanceof HeadsetProfile) ||
-                                   (profile instanceof HfpClientProfile)) {
-                            hfpNotConnected = true;
-                        } else if (profile instanceof  HearingAidProfile) {
-                            hearingAidNotConnected = true;
+                                (profile instanceof HfpClientProfile)) {
+                            hfpConnected = false;
+                        } else if (profile instanceof HearingAidProfile) {
+                            hearingAidConnected = false;
                         }
                     }
                     break;
@@ -1019,65 +1029,50 @@
                     com.android.settingslib.Utils.formatPercentage(batteryLevel);
         }
 
-        // Prepare the string for the Active Device summary
-        String[] activeDeviceStringsArray = mContext.getResources().getStringArray(
-                R.array.bluetooth_audio_active_device_summaries);
-        String activeDeviceString = activeDeviceStringsArray[0];  // Default value: not active
-        if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
-            activeDeviceString = activeDeviceStringsArray[1];     // Active for Media and Phone
-        } else {
-            if (mIsActiveDeviceA2dp) {
-                activeDeviceString = activeDeviceStringsArray[2]; // Active for Media only
-            }
-            if (mIsActiveDeviceHeadset) {
-                activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
-            }
-        }
-        if (!hearingAidNotConnected && mIsActiveDeviceHearingAid) {
-            activeDeviceString = activeDeviceStringsArray[1];
-            return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
-        }
-
+        int stringRes = R.string.bluetooth_pairing;
+        //when profile is connected, information would be available
         if (profileConnected) {
-            if (a2dpNotConnected && hfpNotConnected) {
+            if (a2dpConnected || hfpConnected || hearingAidConnected) {
+                //contain battery information
                 if (batteryLevelPercentageString != null) {
-                    return mContext.getString(
-                            R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
-                            batteryLevelPercentageString, activeDeviceString);
+                    //device is in phone call
+                    if (com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)) {
+                        if (mIsActiveDeviceHeadset) {
+                            stringRes = R.string.bluetooth_active_battery_level;
+                        } else {
+                            stringRes = R.string.bluetooth_battery_level;
+                        }
+                    } else {//device is not in phone call(ex. idle or playing media)
+                        //need to check if A2DP and HearingAid are exclusive
+                        if (mIsActiveDeviceHearingAid || mIsActiveDeviceA2dp) {
+                            stringRes = R.string.bluetooth_active_battery_level;
+                        } else {
+                            stringRes = R.string.bluetooth_battery_level;
+                        }
+                    }
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp,
-                            activeDeviceString);
+                    //no battery information
+                    if (com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)) {
+                        if (mIsActiveDeviceHeadset) {
+                            stringRes = R.string.bluetooth_active_no_battery_level;
+                        }
+                    } else {
+                        if (mIsActiveDeviceHearingAid || mIsActiveDeviceA2dp) {
+                            stringRes = R.string.bluetooth_active_no_battery_level;
+                        }
+                    }
                 }
-
-            } else if (a2dpNotConnected) {
+            } else {//unknown profile with battery information
                 if (batteryLevelPercentageString != null) {
-                    return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
-                            batteryLevelPercentageString, activeDeviceString);
-                } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_a2dp,
-                            activeDeviceString);
-                }
-
-            } else if (hfpNotConnected) {
-                if (batteryLevelPercentageString != null) {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
-                            batteryLevelPercentageString, activeDeviceString);
-                } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset,
-                            activeDeviceString);
-                }
-            } else {
-                if (batteryLevelPercentageString != null) {
-                    return mContext.getString(R.string.bluetooth_connected_battery_level,
-                            batteryLevelPercentageString, activeDeviceString);
-                } else {
-                    return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+                    stringRes = R.string.bluetooth_battery_level;
                 }
             }
         }
 
-        return getBondState() == BluetoothDevice.BOND_BONDING ?
-                mContext.getString(R.string.bluetooth_pairing) : null;
+        return (stringRes != R.string.bluetooth_pairing
+                || getBondState() == BluetoothDevice.BOND_BONDING)
+                ? mContext.getString(stringRes, batteryLevelPercentageString)
+                : null;
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 3b1e4ce..15f6983 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -358,6 +358,12 @@
         }
     }
 
+    public synchronized void dispatchAudioModeChanged() {
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            cachedDevice.onAudioModeChanged();
+        }
+    }
+
     private void log(String msg) {
         if (DEBUG) {
             Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 5f7ba586..38c8a88 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -24,6 +24,7 @@
 import android.os.ParcelUuid;
 import android.util.Log;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -248,4 +249,8 @@
     public int getMaxConnectedAudioDevices() {
         return mAdapter.getMaxConnectedAudioDevices();
     }
+
+    public List<Integer> getSupportedProfiles() {
+        return mAdapter.getSupportedProfiles();
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index b499fda..1c7c855 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -40,6 +40,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 
@@ -158,9 +159,13 @@
         addProfile(mPbapProfile, PbapServerProfile.NAME,
              BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
 
-        mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
-        addProfile(mHearingAidProfile, HearingAidProfile.NAME,
-                   BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+        List<Integer> supportedList = mLocalAdapter.getSupportedProfiles();
+        if (supportedList.contains(BluetoothProfile.HEARING_AID)) {
+            mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager,
+                                                       this);
+            addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+                       BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+        }
         if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
index eb9bbf5..026bbd4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
@@ -48,6 +48,7 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "InputMethdAndSubtypeUtl";
 
+    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
     private static final char INPUT_METHOD_SEPARATER = ':';
     private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
     private static final int NOT_A_SUBTYPE_ID = -1;
@@ -177,7 +178,7 @@
             final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
             final boolean systemIme = imi.isSystem();
             if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
-                    context.getActivity()).isAlwaysCheckedIme(imi, context.getActivity()))
+                    context.getActivity()).isAlwaysCheckedIme(imi))
                     || isImeChecked) {
                 if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
                     // imiId has just been enabled
@@ -416,4 +417,19 @@
         }
         return configurationLocale;
     }
+
+    public static boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi) {
+        if (imi.isAuxiliaryIme() || !imi.isSystem()) {
+            return false;
+        }
+        final int subtypeCount = imi.getSubtypeCount();
+        for (int i = 0; i < subtypeCount; ++i) {
+            final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+            if (SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
+                    && subtype.isAsciiCapable()) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 442a35f..2dec6c3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -124,7 +124,7 @@
         }
         mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context);
         mHasPriorityInSorting = imi.isSystem()
-                && mInputMethodSettingValues.isValidSystemNonAuxAsciiCapableIme(imi, context);
+                && InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(imi);
         setOnPreferenceClickListener(this);
         setOnPreferenceChangeListener(this);
     }
@@ -197,8 +197,7 @@
     }
 
     public void updatePreferenceViews() {
-        final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme(
-                mImi, getContext());
+        final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme(mImi);
         // When this preference has a switch and an input method should be always enabled,
         // this preference should be disabled to prevent accidentally disabling an input method.
         // This preference should also be disabled in case the admin does not allow this input
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index ab017e2..b6786d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -22,15 +22,11 @@
 import android.util.Log;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Locale;
 
 /**
  * This class is a wrapper for {@link InputMethodManager} and
@@ -47,7 +43,6 @@
     private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     private final ContentResolver mContentResolver;
     private final InputMethodManager mImm;
-    private final HashSet<InputMethodInfo> mAsciiCapableEnabledImis = new HashSet<>();
 
     public static InputMethodSettingValuesWrapper getInstance(Context context) {
         if (sInstance == null) {
@@ -70,50 +65,32 @@
     public void refreshAllInputMethodAndSubtypes() {
         mMethodList.clear();
         mMethodList.addAll(mImm.getInputMethodList());
-        updateAsciiCapableEnabledImis();
-    }
-
-    // TODO: Add a cts to ensure at least one AsciiCapableSubtypeEnabledImis exist
-    private void updateAsciiCapableEnabledImis() {
-        mAsciiCapableEnabledImis.clear();
-        final List<InputMethodInfo> enabledImis = getEnabledInputMethodList();
-        for (final InputMethodInfo imi : enabledImis) {
-            final int subtypeCount = imi.getSubtypeCount();
-            for (int i = 0; i < subtypeCount; ++i) {
-                final InputMethodSubtype subtype = imi.getSubtypeAt(i);
-                if (InputMethodUtils.SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
-                        && subtype.isAsciiCapable()) {
-                    mAsciiCapableEnabledImis.add(imi);
-                    break;
-                }
-            }
-        }
     }
 
     public List<InputMethodInfo> getInputMethodList() {
         return new ArrayList<>(mMethodList);
     }
 
-    public boolean isAlwaysCheckedIme(InputMethodInfo imi, Context context) {
+    public boolean isAlwaysCheckedIme(InputMethodInfo imi) {
         final boolean isEnabled = isEnabledImi(imi);
         if (getEnabledInputMethodList().size() <= 1 && isEnabled) {
             return true;
         }
 
         final int enabledValidSystemNonAuxAsciiCapableImeCount =
-                getEnabledValidSystemNonAuxAsciiCapableImeCount(context);
+                getEnabledValidSystemNonAuxAsciiCapableImeCount();
 
         return enabledValidSystemNonAuxAsciiCapableImeCount <= 1
                 && !(enabledValidSystemNonAuxAsciiCapableImeCount == 1 && !isEnabled)
                 && imi.isSystem()
-                && isValidSystemNonAuxAsciiCapableIme(imi, context);
+                && InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(imi);
     }
 
-    private int getEnabledValidSystemNonAuxAsciiCapableImeCount(Context context) {
+    private int getEnabledValidSystemNonAuxAsciiCapableImeCount() {
         int count = 0;
         final List<InputMethodInfo> enabledImis = getEnabledInputMethodList();
         for (final InputMethodInfo imi : enabledImis) {
-            if (isValidSystemNonAuxAsciiCapableIme(imi, context)) {
+            if (InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(imi)) {
                 ++count;
             }
         }
@@ -133,25 +110,6 @@
         return false;
     }
 
-    public boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi, Context context) {
-        if (imi.isAuxiliaryIme()) {
-            return false;
-        }
-        final Locale systemLocale = context.getResources().getConfiguration().locale;
-        if (InputMethodUtils.isSystemImeThatHasSubtypeOf(imi, context,
-                    true /* checkDefaultAttribute */, systemLocale, false /* checkCountry */,
-                    InputMethodUtils.SUBTYPE_MODE_ANY)) {
-            return true;
-        }
-        if (mAsciiCapableEnabledImis.isEmpty()) {
-            Log.w(TAG, "ascii capable subtype enabled imi not found. Fall back to English"
-                    + " Keyboard subtype.");
-            return InputMethodUtils.containsSubtypeOf(imi, Locale.ENGLISH, false /* checkCountry */,
-                    InputMethodUtils.SUBTYPE_MODE_KEYBOARD);
-        }
-        return mAsciiCapableEnabledImis.contains(imi);
-    }
-
     /**
      * Returns the list of the enabled {@link InputMethodInfo} determined by
      * {@link android.provider.Settings.Secure#ENABLED_INPUT_METHODS} rather than just returning
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
index 83c00ea..bb43581 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
@@ -23,7 +23,6 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.text.Collator;
 import java.util.Locale;
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
index 113256f..e2faf6a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
@@ -354,20 +354,15 @@
             return;
         }
 
-        if (userInfo.isGuest()) {
-            switchToGuest(userInfo.name);
-            return;
-        }
-
         switchToUserId(userInfo.id);
     }
 
     /**
-     * Creates a guest session and switches into the guest session.
+     * Creates a new guest session and switches into the guest session.
      *
      * @param guestName Username for the guest user.
      */
-    public void switchToGuest(String guestName) {
+    public void startNewGuestSession(String guestName) {
         UserInfo guest = mUserManager.createGuest(mContext, guestName);
         if (guest == null) {
             // Couldn't create user, most likely because there are too many, but we haven't
@@ -375,6 +370,7 @@
             Log.w(TAG, "can't create user.");
             return;
         }
+        assignDefaultIcon(guest);
         switchToUserId(guest.id);
     }
 
@@ -417,6 +413,27 @@
         mUserManager.setUserName(user.id, name);
     }
 
+    /**
+     * Gets a bitmap representing the user's default avatar.
+     *
+     * @param userInfo User whose avatar should be returned.
+     * @return Default user icon
+     */
+    public Bitmap getUserDefaultIcon(UserInfo userInfo) {
+        return UserIcons.convertToBitmap(
+                UserIcons.getDefaultUserIcon(mContext.getResources(), userInfo.id, false));
+    }
+
+    /**
+     * Gets a bitmap representing the default icon for a Guest user.
+     *
+     * @return Degault guest icon
+     */
+    public Bitmap getGuestDefaultIcon() {
+        return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
+                mContext.getResources(), UserHandle.USER_NULL, false));
+    }
+
     private void registerReceiver() {
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -435,8 +452,7 @@
      * @return Bitmap that has been assigned to the user.
      */
     private Bitmap assignDefaultIcon(UserInfo userInfo) {
-        Bitmap bitmap = UserIcons.convertToBitmap(
-                UserIcons.getDefaultUserIcon(mContext.getResources(), userInfo.id, false));
+        Bitmap bitmap = userInfo.isGuest() ? getGuestDefaultIcon() : getUserDefaultIcon(userInfo);
         mUserManager.setUserIcon(userInfo.id, bitmap);
         return bitmap;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java
deleted file mode 100644
index 17e3401..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/BluetoothA2dpWrapper.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.wrapper;
-
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothCodecStatus;
-import android.bluetooth.BluetoothDevice;
-
-/**
- * This class replicates some methods of android.bluetooth.BluetoothA2dp that are new and not
- * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
- * methods in production and a mock in tests.
- */
-public class BluetoothA2dpWrapper {
-
-    private BluetoothA2dp mService;
-
-    public BluetoothA2dpWrapper(BluetoothA2dp service) {
-        mService = service;
-    }
-
-    /**
-     * @return the real {@code BluetoothA2dp} object
-     */
-    public BluetoothA2dp getService() {
-        return mService;
-    }
-
-    /**
-     * Wraps {@code BluetoothA2dp.getCodecStatus}
-     */
-    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
-        return mService.getCodecStatus(device);
-    }
-
-    /**
-     * Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
-     */
-    public int supportsOptionalCodecs(BluetoothDevice device) {
-        return mService.supportsOptionalCodecs(device);
-    }
-
-    /**
-     * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled}
-     */
-    public int getOptionalCodecsEnabled(BluetoothDevice device) {
-        return mService.getOptionalCodecsEnabled(device);
-    }
-
-    /**
-     * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled}
-     */
-    public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
-        mService.setOptionalCodecsEnabled(device, value);
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
deleted file mode 100644
index 1a268a6..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.wrapper;
-
-import android.location.LocationManager;
-import android.os.UserHandle;
-
-/**
- * This class replicates some methods of android.location.LocationManager that are new and not
- * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
- * methods in production and a mock in tests.
- */
-public class LocationManagerWrapper {
-
-    private LocationManager mLocationManager;
-
-    public LocationManagerWrapper(LocationManager locationManager) {
-        mLocationManager = locationManager;
-    }
-
-    /** Returns the real {@code LocationManager} object */
-    public LocationManager getLocationManager() {
-        return mLocationManager;
-    }
-
-    /** Wraps {@code LocationManager.isProviderEnabled} method */
-    public boolean isProviderEnabled(String provider) {
-        return mLocationManager.isProviderEnabled(provider);
-    }
-
-    /** Wraps {@code LocationManager.setProviderEnabledForUser} method */
-    public void setProviderEnabledForUser(String provider, boolean enabled, UserHandle userHandle) {
-        mLocationManager.setProviderEnabledForUser(provider, enabled, userHandle);
-    }
-
-    /** Wraps {@code LocationManager.isLocationEnabled} method */
-    public boolean isLocationEnabled() {
-        return mLocationManager.isLocationEnabled();
-    }
-
-    /** Wraps {@code LocationManager.isLocationEnabledForUser} method */
-    public boolean isLocationEnabledForUser(UserHandle userHandle) {
-        return mLocationManager.isLocationEnabledForUser(userHandle);
-    }
-
-    /** Wraps {@code LocationManager.setLocationEnabledForUser} method */
-    public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        mLocationManager.setLocationEnabledForUser(enabled, userHandle);
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
deleted file mode 100644
index 4e22473..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.wrapper;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.os.storage.VolumeInfo;
-
-import java.util.List;
-
-/**
- * A thin wrapper class that simplifies testing by putting a mockable layer between the application
- * and the PackageManager. This class only provides access to the minimum number of functions from
- * the PackageManager needed for DeletionHelper to work.
- */
-@Deprecated
-// Please replace with android.content.pm.PackageManager
-public class PackageManagerWrapper {
-
-    private final PackageManager mPm;
-
-    public PackageManagerWrapper(PackageManager pm) {
-        mPm = pm;
-    }
-
-    /**
-     * Returns the real {@code PackageManager} object.
-     */
-    public PackageManager getPackageManager() {
-        return mPm;
-    }
-
-    /**
-     * Calls {@code PackageManager.getInstalledApplicationsAsUser()}.
-     *
-     * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser
-     */
-    public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
-        return mPm.getInstalledApplicationsAsUser(flags, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.getInstalledPackagesAsUser}
-     */
-    public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
-        return mPm.getInstalledPackagesAsUser(flags, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.hasSystemFeature()}.
-     *
-     * @see android.content.pm.PackageManager#hasSystemFeature
-     */
-    public boolean hasSystemFeature(String name) {
-        return mPm.hasSystemFeature(name);
-    }
-
-    /**
-     * Calls {@code PackageManager.queryIntentActivitiesAsUser()}.
-     *
-     * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser
-     */
-    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
-        return mPm.queryIntentActivitiesAsUser(intent, flags, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.getInstallReason()}.
-     *
-     * @see android.content.pm.PackageManager#getInstallReason
-     */
-    public int getInstallReason(String packageName, UserHandle user) {
-        return mPm.getInstallReason(packageName, user);
-    }
-
-    /**
-     * Calls {@code PackageManager.getApplicationInfoAsUser}
-     */
-    public ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
-            throws PackageManager.NameNotFoundException {
-        return mPm.getApplicationInfoAsUser(packageName, i, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.setDefaultBrowserPackageNameAsUser}
-     */
-    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
-        return mPm.setDefaultBrowserPackageNameAsUser(packageName, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.getDefaultBrowserPackageNameAsUser}
-     */
-    public String getDefaultBrowserPackageNameAsUser(int userId) {
-        return mPm.getDefaultBrowserPackageNameAsUser(userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.getHomeActivities}
-     */
-    public ComponentName getHomeActivities(List<ResolveInfo> homeActivities) {
-        return mPm.getHomeActivities(homeActivities);
-    }
-
-    /**
-     * Calls {@code PackageManager.queryIntentServicesAsUser}
-     */
-    public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user) {
-        return mPm.queryIntentServicesAsUser(intent, i, user);
-    }
-
-    /**
-     * Calls {@code PackageManager.queryIntentServices}
-     */
-    public List<ResolveInfo> queryIntentServices(Intent intent, int i) {
-        return mPm.queryIntentServices(intent, i);
-    }
-
-    /**
-     * Calls {@code PackageManager.replacePreferredActivity}
-     */
-    public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
-            ComponentName[] componentNames, ComponentName component) {
-        mPm.replacePreferredActivity(homeFilter, matchCategoryEmpty, componentNames, component);
-    }
-
-    /**
-     * Gets information about a particular package from the package manager.
-     *
-     * @param packageName The name of the package we would like information about.
-     * @param i           additional options flags. see javadoc for
-     *                    {@link PackageManager#getPackageInfo(String, int)}
-     * @return The PackageInfo for the requested package
-     */
-    public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
-        return mPm.getPackageInfo(packageName, i);
-    }
-
-    /**
-     * Retrieves the icon associated with this particular set of ApplicationInfo
-     *
-     * @param info The ApplicationInfo to retrieve the icon for
-     * @return The icon as a drawable.
-     */
-    public Drawable getUserBadgedIcon(ApplicationInfo info) {
-        return mPm.getUserBadgedIcon(mPm.loadUnbadgedItemIcon(info, info),
-                new UserHandle(UserHandle.getUserId(info.uid)));
-    }
-
-    /**
-     * Retrieves the label associated with the particular set of ApplicationInfo
-     *
-     * @param app The ApplicationInfo to retrieve the label for
-     * @return the label as a CharSequence
-     */
-    public CharSequence loadLabel(ApplicationInfo app) {
-        return app.loadLabel(mPm);
-    }
-
-    /**
-     * Retrieve all activities that can be performed for the given intent.
-     */
-    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
-        return mPm.queryIntentActivities(intent, flags);
-    }
-
-    /**
-     * Calls {@code PackageManager.getPrimaryStorageCurrentVolume}
-     */
-    public VolumeInfo getPrimaryStorageCurrentVolume() {
-        return mPm.getPrimaryStorageCurrentVolume();
-    }
-
-    /**
-     * Calls {@code PackageManager.deletePackageAsUser}
-     */
-    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
-            int userId) {
-        mPm.deletePackageAsUser(packageName, observer, flags, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.getPackageUidAsUser}
-     */
-    public int getPackageUidAsUser(String pkg, int userId)
-            throws PackageManager.NameNotFoundException {
-        return mPm.getPackageUidAsUser(pkg, userId);
-    }
-
-    /**
-     * Calls {@code PackageManager.setApplicationEnabledSetting}
-     */
-    public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
-        mPm.setApplicationEnabledSetting(packageName, newState, flags);
-    }
-
-    /**
-     * Calls {@code PackageManager.getApplicationEnabledSetting}
-     */
-    public int getApplicationEnabledSetting(String packageName) {
-        return mPm.getApplicationEnabledSetting(packageName);
-    }
-
-    /**
-     * Calls {@code PackageManager.setComponentEnabledSetting}
-     */
-    public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
-        mPm.setComponentEnabledSetting(componentName, newState, flags);
-    }
-
-    /**
-     * Calls {@code PackageManager.getApplicationInfo}
-     */
-    public ApplicationInfo getApplicationInfo(String packageName, int flags)
-            throws NameNotFoundException {
-        return mPm.getApplicationInfo(packageName, flags);
-    }
-
-    /**
-     * Calls {@code PackageManager.getApplicationLabel}
-     */
-    public CharSequence getApplicationLabel(ApplicationInfo info) {
-        return mPm.getApplicationLabel(info);
-    }
-
-    /**
-     * Calls {@code PackageManager.queryBroadcastReceivers}
-     */
-    public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
-        return mPm.queryBroadcastReceivers(intent, flags);
-    }
-}
-
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
index ab2b97f..93b038e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
@@ -125,6 +125,7 @@
         final InputMethodSubtype systemLocaleSubtype =
                 new InputMethodSubtype.InputMethodSubtypeBuilder()
                         .setIsAsciiCapable(true)
+                        .setSubtypeMode("keyboard")
                         .setSubtypeLocale(systemLocale.getLanguage())
                         .build();
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
index 15f7770..54510b2 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
@@ -247,13 +247,13 @@
     }
 
     @Test
-    public void switchToGuest() {
-        mHelper.switchToGuest("Test Guest");
+    public void startNewGuestSession() {
+        mHelper.startNewGuestSession("Test Guest");
         verify(mUserManager).createGuest(mContext, "Test Guest");
 
         UserInfo guestInfo = new UserInfo(21, "Test Guest", UserInfo.FLAG_GUEST);
         when(mUserManager.createGuest(mContext, "Test Guest")).thenReturn(guestInfo);
-        mHelper.switchToGuest("Test Guest");
+        mHelper.startNewGuestSession("Test Guest");
         verify(mActivityManager).switchUser(21);
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 706d0c0..09a2bd2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -33,14 +34,13 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.location.LocationManager;
+import android.media.AudioManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
 
-import com.android.settingslib.wrapper.LocationManagerWrapper;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +52,7 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowAudioManager;
 import org.robolectric.shadows.ShadowSettings;
 
 import java.util.HashMap;
@@ -60,7 +61,7 @@
 @RunWith(SettingsLibRobolectricTestRunner.class)
 @Config(shadows = {
             UtilsTest.ShadowSecure.class,
-            UtilsTest.ShadowLocationManagerWrapper.class})
+            UtilsTest.ShadowLocationManager.class})
 public class UtilsTest {
     private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
     private static final String PERCENTAGE_0 = "0%";
@@ -69,6 +70,7 @@
     private static final String PERCENTAGE_50 = "50%";
     private static final String PERCENTAGE_100 = "100%";
 
+    private ShadowAudioManager mShadowAudioManager;
     private Context mContext;
     @Mock
     private LocationManager mLocationManager;
@@ -79,6 +81,7 @@
         mContext = spy(RuntimeEnvironment.application);
         when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
         ShadowSecure.reset();
+        mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
     }
 
     @Test
@@ -187,12 +190,40 @@
         }
     }
 
-    @Implements(value = LocationManagerWrapper.class)
-    public static class ShadowLocationManagerWrapper {
+    @Implements(value = LocationManager.class)
+    public static class ShadowLocationManager {
 
         @Implementation
         public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
             // Do nothing
         }
     }
+
+    @Test
+    public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+
+        assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAudioModeOngoingCall_modeInCall_returnTrue() {
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+        assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
+        mShadowAudioManager.setMode(AudioManager.MODE_RINGTONE);
+
+        assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAudioModeOngoingCall_modeNormal_returnFalse() {
+        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+
+        assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 334ea16..ef13a5f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -31,7 +31,6 @@
 import android.content.res.Resources;
 
 import com.android.settingslib.R;
-import com.android.settingslib.wrapper.BluetoothA2dpWrapper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,7 +48,6 @@
     @Mock LocalBluetoothProfileManager mProfileManager;
     @Mock BluetoothDevice mDevice;
     @Mock BluetoothA2dp mBluetoothA2dp;
-    @Mock BluetoothA2dpWrapper mBluetoothA2dpWrapper;
     BluetoothProfile.ServiceListener mServiceListener;
 
     A2dpProfile mProfile;
@@ -68,31 +66,30 @@
 
         mProfile = new A2dpProfile(mContext, mAdapter, mDeviceManager, mProfileManager);
         mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
-        mProfile.setBluetoothA2dpWrapper(mBluetoothA2dpWrapper);
     }
 
     @Test
     public void supportsHighQualityAudio() {
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue();
 
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
 
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
     }
 
     @Test
     public void isHighQualityAudioEnabled() {
-        when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
 
-        when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
 
@@ -100,23 +97,23 @@
         // then isHighQualityAudioEnabled() should return true or false based on whether optional
         // codecs are supported. If the device is connected then we should ask it directly, but if
         // the device isn't connected then rely on the stored pref about such support.
-        when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
         when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
                 BluetoothProfile.STATE_DISCONNECTED);
 
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
 
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
 
         when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
                 BluetoothProfile.STATE_CONNECTED);
         BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
-        when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
+        when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
         BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
         when(status.getCodecConfig()).thenReturn(config);
         when(config.isMandatoryCodec()).thenReturn(false);
@@ -151,14 +148,14 @@
 
         // Most tests want to simulate optional codecs being supported by the device, so do that
         // by default here.
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
     }
 
     @Test
     public void getLableCodecsNotSupported() {
         setupLabelTest();
-        when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
     }
@@ -179,7 +176,7 @@
         BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
         BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
         BluetoothCodecConfig[] configs = {config};
-        when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
+        when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
         when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
 
         when(config.isMandatoryCodec()).thenReturn(true);
@@ -194,7 +191,7 @@
         BluetoothCodecStatus status = mock(BluetoothCodecStatus.class);
         BluetoothCodecConfig config = mock(BluetoothCodecConfig.class);
         BluetoothCodecConfig[] configs = {config};
-        when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status);
+        when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status);
         when(status.getCodecsSelectableCapabilities()).thenReturn(configs);
 
         when(config.isMandatoryCodec()).thenReturn(false);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 7863fc5..5853dca 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -23,23 +23,25 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.media.AudioManager;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowAudioManager;
 
-@RunWith(RobolectricTestRunner.class)
-@Config(resourceDir = "../../res")
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class CachedBluetoothDeviceTest {
     private final static String DEVICE_NAME = "TestName";
     private final static String DEVICE_ALIAS = "TestAlias";
@@ -60,6 +62,7 @@
     @Mock
     private BluetoothDevice mDevice;
     private CachedBluetoothDevice mCachedDevice;
+    private ShadowAudioManager mShadowAudioManager;
     private Context mContext;
     private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
 
@@ -67,6 +70,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
+        mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
         when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
         when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
         when(mHfpProfile.isProfileReady()).thenReturn(true);
@@ -83,7 +87,7 @@
         // Test without battery level
         // Set PAN profile to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set PAN profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -93,7 +97,7 @@
         mBatteryLevel = 10;
         // Set PAN profile to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
 
         // Set PAN profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -104,7 +108,7 @@
 
         // Set PAN profile to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set PAN profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -119,23 +123,23 @@
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
 
         // Disconnect HFP only and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected (no phone), battery 10%");
+                "10% battery");
 
         // Disconnect A2DP only and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected (no media), battery 10%");
+                "10% battery");
 
         // Disconnect both HFP and A2DP and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected (no phone or media), battery 10%");
+                "10% battery");
 
         // Disconnect all profiles and test connection state summary
         mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -147,16 +151,16 @@
         // Test without battery level
         // Set A2DP profile to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for A2DP and test connection state summary
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Test with battery level
         mBatteryLevel = 10;
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected, battery 10%, active(media)");
+       assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+                "Active, 10% battery");
 
         // Set A2DP profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -167,7 +171,7 @@
         // Set A2DP profile to be connected, Active and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set A2DP profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -179,16 +183,18 @@
         // Test without battery level
         // Set HFP profile to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for HFP and test connection state summary
+        mCachedDevice.onAudioModeChanged();
+        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Test with battery level
         mBatteryLevel = 10;
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected, battery 10%, active(phone)");
+       assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+                "Active, 10% battery");
 
         // Set HFP profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -199,7 +205,7 @@
         // Set HFP profile to be connected, Active and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set HFP profile to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -211,15 +217,16 @@
         // Test without battery level
         // Set Hearing Aid profile to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for Hearing Aid and test connection state summary
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set Hearing Aid profile to be disconnected and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
-        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
+        mCachedDevice.
+                onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
     }
 
@@ -229,23 +236,23 @@
         // Set A2DP and HFP profiles to be connected and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for A2DP and HFP and test connection state summary
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Test with battery level
         mBatteryLevel = 10;
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected, battery 10%, active");
+                "Active, 10% battery");
 
         // Disconnect A2DP only and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected (no media), battery 10%, active(phone)");
+                "10% battery");
 
         // Disconnect HFP only and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
@@ -253,7 +260,7 @@
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Connected (no phone), battery 10%, active(media)");
+                "Active, 10% battery");
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -262,7 +269,7 @@
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set A2DP and HFP profiles to be disconnected and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 0eec247..d0a0686 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -22,6 +22,14 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
@@ -187,4 +195,75 @@
                 + "|ime3;subtype2;subtype3:ime0;subtype1;subtype0"
                 + "|ime4;subtype3;subtype2:ime0;subtype1;subtype0");
     }
+
+    @Test
+    public void isValidSystemNonAuxAsciiCapableIme() {
+        // System IME w/ no subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false)))
+                .isFalse();
+
+        // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+                .isFalse();
+
+        // System IME w/ non-Aux and ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+                .isTrue();
+
+        // System IME w/ Aux and ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+                .isFalse();
+
+        // System IME w/ non-Aux and ASCII-capable "voice" subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false, createDummySubtype("voice", false, true))))
+                .isFalse();
+
+        // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(true, false,
+                        createDummySubtype("keyboard", false, true),
+                        createDummySubtype("keyboard", false, false))))
+                .isTrue();
+
+        // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
+        assertThat(InputMethodAndSubtypeUtil.isValidSystemNonAuxAsciiCapableIme(
+                createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+                .isFalse();
+   }
+
+    private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+            InputMethodSubtype... subtypes) {
+        final ResolveInfo ri = new ResolveInfo();
+        final ServiceInfo si = new ServiceInfo();
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = "com.example.android.dummyime";
+        ai.enabled = true;
+        ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
+        si.applicationInfo = ai;
+        si.enabled = true;
+        si.packageName = "com.example.android.dummyime";
+        si.name = "Dummy IME";
+        si.exported = true;
+        si.nonLocalizedLabel = "Dummy IME";
+        ri.serviceInfo = si;
+        return new InputMethodInfo(ri, isAuxIme, "",  Arrays.asList(subtypes), 1, false);
+    }
+
+    private static InputMethodSubtype createDummySubtype(
+            String mode, boolean isAuxiliary, boolean isAsciiCapable) {
+        return new InputMethodSubtypeBuilder()
+                .setSubtypeNameResId(0)
+                .setSubtypeIconResId(0)
+                .setSubtypeLocale("en_US")
+                .setLanguageTag("en-US")
+                .setSubtypeMode(mode)
+                .setIsAuxiliary(isAuxiliary)
+                .setIsAsciiCapable(isAsciiCapable)
+                .build();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index f2ea3a4..890abef 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -185,12 +185,7 @@
         mHelper.switchToUser(createUserInfoForId(20));
         assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isFalse();
 
-        // Switching to Guest calls createGuest.
-        UserInfo guestInfo = new UserInfo(21, "Test Guest", UserInfo.FLAG_GUEST);
-        mHelper.switchToUser(guestInfo);
-        verify(mUserManager).createGuest(mContext, "Test Guest");
-
-        // Switching to non-current, non-guest user, simply calls switchUser.
+        // Switching to non-foreground user, simply calls switchUser.
         UserInfo userToSwitchTo = new UserInfo(22, "Test User", 0);
         mHelper.switchToUser(userToSwitchTo);
         assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isTrue();
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index db57fd1..ccde571 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -3,6 +3,8 @@
 
 LOCAL_MODULE_TAGS := optional
 
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     src/com/android/providers/settings/EventLogTags.logtags
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 4c98bb8..7a994a8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -17,7 +17,6 @@
 package com.android.providers.settings;
 
 import android.os.Process;
-import com.android.internal.R;
 import com.android.internal.app.LocalePicker;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -30,13 +29,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.hardware.display.DisplayManager;
 import android.icu.util.ULocale;
 import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.RingtoneManager;
 import android.net.Uri;
-import android.os.IPowerManager;
 import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -46,16 +43,16 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.Slog;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Locale;
 
 public class SettingsHelper {
     private static final String TAG = "SettingsHelper";
     private static final String SILENT_RINGTONE = "_silent";
+    private static final float FLOAT_TOLERANCE = 0.01f;
+
     private Context mContext;
     private AudioManager mAudioManager;
     private TelephonyManager mTelephonyManager;
@@ -259,9 +256,14 @@
             case Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES:
             case Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES:
             case Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
-            case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE:
                 return !TextUtils.isEmpty(Settings.Secure.getString(
                         mContext.getContentResolver(), name));
+            case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE:
+                float defaultScale = mContext.getResources().getFraction(
+                        R.fraction.def_accessibility_display_magnification_scale, 1, 1);
+                float currentScale = Settings.Secure.getFloat(
+                        mContext.getContentResolver(), name, defaultScale);
+                return Math.abs(currentScale - defaultScale) >= FLOAT_TOLERANCE;
             case Settings.System.FONT_SCALE:
                 return Settings.System.getFloat(mContext.getContentResolver(), name, 1.0f) != 1.0f;
             default:
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index bd5b1f2..0d681ed 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -14,6 +14,10 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.base
 
+LOCAL_RESOURCE_DIR := frameworks/base/packages/SettingsProvider/res
+
+LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.providers.settings
+
 LOCAL_PACKAGE_NAME := SettingsProviderTest
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
new file mode 100644
index 0000000..b5324e5
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SettingsHelper#restoreValue(Context, ContentResolver, ContentValues, Uri,
+ * String, String, int)}. Specifically verifies that we restore critical accessibility settings only
+ * if the user has not already configured these in SUW.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsHelperRestoreTest {
+    private static final float FLOAT_TOLERANCE = 0.01f;
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+    private SettingsHelper mSettingsHelper;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getContext();
+        mContentResolver = mContext.getContentResolver();
+        mSettingsHelper = new SettingsHelper(mContext);
+    }
+
+    /** Tests for {@link Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. */
+    @Test
+    public void
+            testRestoreAccessibilityDisplayMagnificationScale_alreadyConfigured_doesNotRestoreValue()
+                    throws Exception {
+        float defaultSettingValue = setDefaultAccessibilityDisplayMagnificationScale();
+        String settingName = Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE;
+        float restoreSettingValue = defaultSettingValue + 0.5f;
+
+        // Simulate already configuring setting via SUW.
+        float configuredSettingValue = defaultSettingValue + 1.0f;
+        Settings.Secure.putFloat(mContentResolver, settingName, configuredSettingValue);
+
+        mSettingsHelper.restoreValue(
+                mContext,
+                mContentResolver,
+                new ContentValues(2),
+                Settings.Secure.getUriFor(settingName),
+                settingName,
+                String.valueOf(restoreSettingValue),
+                Build.VERSION.SDK_INT);
+
+        assertEquals(
+                configuredSettingValue,
+                Settings.Secure.getFloat(mContentResolver, settingName),
+                FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void
+            testRestoreAccessibilityDisplayMagnificationScale_notAlreadyConfigured_restoresValue()
+                    throws Exception {
+        float defaultSettingValue = setDefaultAccessibilityDisplayMagnificationScale();
+        String settingName = Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE;
+        float restoreSettingValue = defaultSettingValue + 0.5f;
+
+        mSettingsHelper.restoreValue(
+                mContext,
+                mContentResolver,
+                new ContentValues(2),
+                Settings.Secure.getUriFor(settingName),
+                settingName,
+                String.valueOf(restoreSettingValue),
+                Build.VERSION.SDK_INT);
+
+        assertEquals(
+                restoreSettingValue,
+                Settings.Secure.getFloat(mContentResolver, settingName),
+                FLOAT_TOLERANCE);
+    }
+
+    /**
+     * Simulate {@link Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE} value at boot by
+     * loading the default.
+     *
+     * @return the default value.
+     */
+    private float setDefaultAccessibilityDisplayMagnificationScale() {
+        float defaultSettingValue =
+                mContext.getResources()
+                        .getFraction(
+                                R.fraction.def_accessibility_display_magnification_scale, 1, 1);
+        Settings.Secure.putFloat(
+                mContentResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+                defaultSettingValue);
+        return defaultSettingValue;
+    }
+}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 6dea493..3193101 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -46,8 +46,7 @@
                 style="@style/widget_big_thin"
                 android:format12Hour="@string/keyguard_widget_12_hours_format"
                 android:format24Hour="@string/keyguard_widget_24_hours_format"
-                android:baselineAligned="true"
-                android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+                android:baselineAligned="true" />
 
             <include layout="@layout/keyguard_status_area" />
             <ImageView
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 611aa43..db03ed2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,12 +28,14 @@
     android:clipToPadding="false"
     android:orientation="vertical"
     android:layout_centerHorizontal="true">
-    <TextView android:id="@+id/title"
+    <com.android.systemui.statusbar.AlphaOptimizedTextView
+              android:id="@+id/title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_marginBottom="7dp"
+              android:layout_marginBottom="@dimen/widget_title_bottom_margin"
               android:paddingStart="64dp"
               android:paddingEnd="64dp"
+              android:visibility="gone"
               android:textColor="?attr/wallpaperTextColor"
               android:theme="@style/TextAppearance.Keyguard"
     />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index fa14d1b..ba05ccf 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -67,21 +67,17 @@
                 android:singleLine="true"
                 style="@style/widget_big_thin"
                 android:format12Hour="@string/keyguard_widget_12_hours_format"
-                android:format24Hour="@string/keyguard_widget_24_hours_format"
-                android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+                android:format24Hour="@string/keyguard_widget_24_hours_format" />
             <View
                 android:id="@+id/clock_separator"
                 android:layout_width="@dimen/widget_separator_width"
                 android:layout_height="@dimen/widget_separator_thickness"
-                android:layout_marginTop="22dp"
                 android:layout_below="@id/clock_view"
                 android:background="#f00"
-                android:backgroundTint="?attr/wallpaperTextColor"
                 android:layout_centerHorizontal="true" />
 
             <include layout="@layout/keyguard_status_area"
                 android:id="@+id/keyguard_status_area"
-                android:layout_marginTop="20dp"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_below="@id/clock_separator" />
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index 4ae7cb9..fdca44d 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -28,7 +28,6 @@
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">100dp</dimen>
     <dimen name="widget_label_font_size">16sp</dimen>
-    <dimen name="bottom_text_spacing_digital">-1dp</dimen>
 
     <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
          Should be 0 on devices with plenty of room (e.g. tablets) -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 37de433..712b6e5 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -41,14 +41,14 @@
          Should be 0 on devices with plenty of room (e.g. tablets) -->
     <dimen name="eca_overlap">-10dip</dimen>
 
-    <!-- Default clock parameters -->
-    <dimen name="bottom_text_spacing_digital">-24dp</dimen>
     <!-- Slice header -->
-    <dimen name="widget_title_font_size">24sp</dimen>
-    <!-- Slice subtitle  -->
-    <dimen name="widget_label_font_size">16sp</dimen>
+    <dimen name="widget_title_font_size">24dp</dimen>
+    <dimen name="widget_title_bottom_margin">7dp</dimen>
+    <dimen name="bottom_text_spacing_digital">0dp</dimen>
+    <!-- Slice subtitle -->
+    <dimen name="widget_label_font_size">16dp</dimen>
     <!-- Slice offset when pulsing -->
-    <dimen name="widget_pulsing_bottom_padding">24dp</dimen>
+    <dimen name="widget_pulsing_bottom_padding">48dp</dimen>
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
     <!-- Clock with header -->
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 5f52e2a..b90b4dd 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -53,6 +53,7 @@
     </style>
     <style name="widget_big_thin">
         <item name="android:textSize">@dimen/widget_big_font_size</item>
+        <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
         <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
     </style>
@@ -89,6 +90,7 @@
     <style name="TextAppearance.Keyguard.Secondary">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
+        <item name="android:lines">1</item>
         <item name="android:textSize">@dimen/widget_label_font_size</item>
     </style>
 
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml b/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
new file mode 100644
index 0000000..74f38d4
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:ordering="together">
+  <objectAnimator
+      android:propertyName="rotation"
+      android:duration="0"
+      android:valueFrom="180"
+      android:valueTo="0"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="alpha"
+      android:valueFrom="0.0"
+      android:valueTo="1.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="scaleX"
+      android:valueFrom="0.8"
+      android:valueTo="1.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="scaleY"
+      android:valueFrom="0.8"
+      android:valueTo="1.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml b/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
new file mode 100644
index 0000000..0f28297
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:ordering="together">
+  <objectAnimator
+      android:propertyName="rotation"
+      android:duration="0"
+      android:valueFrom="0"
+      android:valueTo="180"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="alpha"
+      android:valueFrom="0.0"
+      android:valueTo="1.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="scaleX"
+      android:valueFrom="0.8"
+      android:valueTo="1.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="scaleY"
+      android:valueFrom="0.8"
+      android:valueTo="1.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_out.xml b/packages/SystemUI/res/anim/car_arrow_fade_out.xml
new file mode 100644
index 0000000..e6757d2
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_arrow_fade_out.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:ordering="together">
+  <objectAnimator
+      android:propertyName="alpha"
+      android:valueFrom="1.0"
+      android:valueTo="0.0"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="scaleX"
+      android:valueFrom="1.0"
+      android:valueTo="0.8"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+  <objectAnimator
+      android:propertyName="scaleY"
+      android:valueFrom="1.0"
+      android:valueTo="0.8"
+      android:valueType="floatType"
+      android:duration="300"
+      android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml b/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
new file mode 100644
index 0000000..3709aa5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~
+  ~ Copyright (C) 2018 Google Inc.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="44dp"
+    android:height="44dp"
+    android:viewportWidth="48.0"
+    android:viewportHeight="48.0">
+  <path
+      android:pathData="M14.83 16.42L24 25.59l9.17-9.17L36 19.25l-12 12-12-12z"
+      android:fillColor="#ffffff"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_music.xml b/packages/SystemUI/res/drawable/car_ic_music.xml
new file mode 100644
index 0000000..f90cd69
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_music.xml
@@ -0,0 +1,30 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="56dp"
+    android:height="56dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+  <path
+      android:fillAlpha=".1"
+      android:strokeAlpha=".1"
+      android:pathData="M0 0h48v48H0z" />
+  <path
+      android:fillColor="@color/car_grey_50"
+      android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14
+14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/car_ic_navigation.xml b/packages/SystemUI/res/drawable/car_ic_navigation.xml
new file mode 100644
index 0000000..328efa0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_navigation.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="37dp"
+    android:viewportWidth="32.0"
+    android:viewportHeight="37.0">
+  <path
+      android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
+      android:strokeColor="#00000000"
+      android:fillType="evenOdd"
+      android:fillColor="@color/car_grey_50"
+      android:strokeWidth="1"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification_2.xml b/packages/SystemUI/res/drawable/car_ic_notification_2.xml
new file mode 100644
index 0000000..c74ae15
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_notification_2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="38dp"
+    android:viewportWidth="32"
+    android:viewportHeight="38" >
+  <group
+      android:translateX="-6"
+      android:translateY="-3">
+    <path
+        android:pathData="M26.6195649,6.98115478 C31.5083629,8.85235985 34.9817444,13.6069337 34.9817444,19.1767606 L34.9817444,27.9542254 L38,27.9542254 L38,34.2161972 L6,34.2161972 L6,27.9542254 L9.01825558,27.9542254 L9.01825558,19.1767606 C9.01825558,13.6069337 12.4916371,8.85235985 17.3804351,6.98115478 C17.723241,4.726863 19.6609451,3 22,3 C24.3390549,3 26.276759,4.726863 26.6195649,6.98115478 Z M17.326572,36.3035211 L26.673428,36.3035211 C26.673428,38.8973148 24.581063,41 22,41 C19.418937,41 17.326572,38.8973148 17.326572,36.3035211 Z"
+        android:strokeColor="#00000000"
+        android:fillType="evenOdd"
+        android:fillColor="@color/car_grey_50" />
+  </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
index 65b9813..d591aa8 100644
--- a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
+++ b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
@@ -17,10 +17,10 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
 
     <path
         android:fillColor="#000000"
-        android:alpha="0.87"
         android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
index c464ba6..93adaa0 100644
--- a/packages/SystemUI/res/drawable/smart_reply_button_background.xml
+++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml
@@ -21,9 +21,9 @@
     <item>
         <inset
             android:insetLeft="0dp"
-            android:insetTop="7dp"
+            android:insetTop="8dp"
             android:insetRight="0dp"
-            android:insetBottom="5dp">
+            android:insetBottom="8dp">
             <shape android:shape="rectangle">
                 <corners android:radius="8dp" />
                 <stroke android:width="1dp" android:color="@color/smart_reply_button_stroke" />
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
index 36bc85d..a6beaa1 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog.xml
@@ -13,26 +13,19 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
+<androidx.car.widget.PagedListView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/volume_dialog"
-    android:clipChildren="false"
+    android:background="@drawable/car_card_rounded_background"
+    android:id="@+id/volume_list"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginStart="@dimen/car_margin"
     android:layout_marginEnd="@dimen/car_margin"
-    android:theme="@style/qs_theme" >
-    <androidx.car.widget.PagedListView
-        android:id="@+id/volume_list"
-        android:background="@drawable/car_rounded_bg_bottom"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:minWidth="@dimen/volume_dialog_panel_width"
-        android:theme="?attr/dialogListTheme"
-        app:dividerStartMargin="@dimen/car_keyline_1"
-        app:dividerEndMargin="@dimen/car_keyline_1"
-        app:gutter="none"
-        app:showPagedListViewDivider="true"
-        app:scrollBarEnabled="false" />
-</FrameLayout>
+    android:minWidth="@dimen/volume_dialog_panel_width"
+    android:theme="@style/Theme.Car.NoActionBar"
+    app:dividerStartMargin="@dimen/car_keyline_1"
+    app:dividerEndMargin="@dimen/car_keyline_1"
+    app:gutter="none"
+    app:showPagedListViewDivider="true"
+    app:scrollBarEnabled="false" />
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index f4501da..bccf207 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -34,7 +34,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:singleLine="true"
-        android:textAppearance="@*android:style/TextAppearance.Material.Notification"
         style="?attr/hybridNotificationTextStyle"
     />
 </com.android.systemui.statusbar.notification.HybridNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hybrid_overflow_number.xml b/packages/SystemUI/res/layout/hybrid_overflow_number.xml
index 792f424..08f8f94 100644
--- a/packages/SystemUI/res/layout/hybrid_overflow_number.xml
+++ b/packages/SystemUI/res/layout/hybrid_overflow_number.xml
@@ -16,10 +16,10 @@
   -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@*android:style/Widget.Material.Notification.Text"
     android:id="@+id/notification_text"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:textAppearance="@*android:style/TextAppearance.Material.Notification"
     android:paddingEnd="@dimen/group_overflow_number_padding"
     android:gravity="end"
     android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
index 98e6e82..a490c4b 100644
--- a/packages/SystemUI/res/layout/smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -26,7 +26,7 @@
         android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
         android:background="@drawable/smart_reply_button_background"
         android:gravity="center"
-        android:fontFamily="sans-serif-medium"
+        android:fontFamily="roboto-medium"
         android:textSize="@dimen/smart_reply_button_font_size"
         android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
         android:textColor="@color/smart_reply_button_text"
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
index 215b230..ac235b7 100644
--- a/packages/SystemUI/res/layout/volume_dnd_icon.xml
+++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml
@@ -15,16 +15,16 @@
 -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="14dp"
-    android:layout_height="14dp"
-    android:layout_marginTop="6dp"
-    android:layout_marginRight="6dp"
-    android:layout_gravity="right|top">
+    android:id="@+id/dnd_icon"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
 
     <ImageView
-        android:id="@+id/dnd_icon"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_width="14dp"
+        android:layout_height="14dp"
+        android:layout_marginTop="6dp"
+        android:layout_marginRight="6dp"
+        android:layout_gravity="right|top"
         android:src="@drawable/ic_dnd"
         android:tint="?android:attr/textColorTertiary"/>
 </FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ac9fb2b..eb71911 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -140,6 +140,9 @@
     notification panel collapses -->
     <dimen name="shelf_appear_translation">42dp</dimen>
 
+    <!-- Vertical translation of pulsing notification animations -->
+    <dimen name="pulsing_notification_appear_translation">10dp</dimen>
+
     <!-- The amount the content shifts upwards when transforming into the icon -->
     <dimen name="notification_icon_transform_content_shift">32dp</dimen>
 
@@ -927,12 +930,13 @@
     <!-- Home button padding for sizing -->
     <dimen name="home_padding">16dp</dimen>
 
-    <!-- Smart reply button -->
+    <!-- Smart reply button. Total height 48dp, visible height 32dp. -->
     <dimen name="smart_reply_button_spacing">8dp</dimen>
-    <dimen name="smart_reply_button_padding_vertical">10dp</dimen>
-    <dimen name="smart_reply_button_padding_horizontal_single_line">16dp</dimen>
-    <dimen name="smart_reply_button_padding_horizontal_double_line">16dp</dimen>
-    <dimen name="smart_reply_button_min_height">32dp</dimen>
+    <dimen name="smart_reply_button_padding_vertical">14dp</dimen>
+    <!-- Note: The following two paddings need to be different until b/78876518 is fixed. -->
+    <dimen name="smart_reply_button_padding_horizontal_single_line">20dp</dimen>
+    <dimen name="smart_reply_button_padding_horizontal_double_line">19dp</dimen>
+    <dimen name="smart_reply_button_min_height">48dp</dimen>
     <dimen name="smart_reply_button_font_size">14sp</dimen>
     <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 804ca2c..1e19534 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -137,7 +137,8 @@
         <item name="android:layout_marginTop">4dp</item>
     </style>
 
-    <style name="hybrid_notification_text">
+    <style name="hybrid_notification_text"
+           parent="@*android:style/Widget.Material.Notification.Text">
         <item name="android:paddingEnd">4dp</item>
     </style>
 
@@ -360,10 +361,6 @@
         parent="@*android:style/TextAppearance.Material.Notification.Info">
     </style>
 
-    <style name="TextAppearance.Material.Notification.HybridNotificationDivider"
-        parent="@*android:style/TextAppearance.Material.Notification">
-    </style>
-
     <style name="SearchPanelCircle">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java
new file mode 100644
index 0000000..e93e78d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import com.android.internal.logging.MetricsLogger;
+
+public class MetricsLoggerCompat {
+
+    private final MetricsLogger mMetricsLogger;
+
+    public MetricsLoggerCompat() {
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    public void action(int category) {
+        mMetricsLogger.action(category);
+    }
+
+    public void action(int category, int value) {
+        mMetricsLogger.action(category, value);
+    }
+
+    public void visible(int category) {
+        mMetricsLogger.visible(category);
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index 8d451c1..5a0dddc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -33,10 +33,21 @@
      * touch slop is when the respected operation will occur when exceeded. Touch slop must be
      * larger than the drag slop.
      */
-    public static final int QUICK_STEP_DRAG_SLOP_PX = convertDpToPixel(10);
-    public static final int QUICK_SCRUB_DRAG_SLOP_PX = convertDpToPixel(20);
-    public static final int QUICK_STEP_TOUCH_SLOP_PX = convertDpToPixel(24);
-    public static final int QUICK_SCRUB_TOUCH_SLOP_PX = convertDpToPixel(35);
+    public static int getQuickStepDragSlopPx() {
+        return convertDpToPixel(10);
+    }
+
+    public static int getQuickScrubDragSlopPx() {
+        return convertDpToPixel(20);
+    }
+
+    public static int getQuickStepTouchSlopPx() {
+        return convertDpToPixel(24);
+    }
+
+    public static int getQuickScrubTouchSlopPx() {
+        return convertDpToPixel(35);
+    }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 07b980e..ed2f831 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -26,6 +26,10 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
+
 import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
 import com.android.systemui.shared.recents.view.RecentsTransition;
 
@@ -58,6 +62,11 @@
     public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
     public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 
+    public static final int NAV_BAR_POS_INVALID = -1;
+    public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
+    public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
+    public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
+
     public static final int ACTIVITY_TYPE_STANDARD = WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 
     public static final int WINDOWING_MODE_UNDEFINED = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -141,4 +150,20 @@
             Log.w(TAG, "Failed to set recents visibility");
         }
     }
+
+    /**
+     * @return The side of the screen where navigation bar is positioned.
+     * @see #NAV_BAR_POS_RIGHT
+     * @see #NAV_BAR_POS_LEFT
+     * @see #NAV_BAR_POS_BOTTOM
+     * @see #NAV_BAR_POS_INVALID
+     */
+    public int getNavBarPosition() {
+        try {
+            return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to get nav bar position");
+        }
+        return NAV_BAR_POS_INVALID;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index e936c3b..5ce3339 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -34,6 +34,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.Animation;
 import android.widget.Button;
 import android.widget.LinearLayout;
@@ -46,8 +47,9 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
 
 import java.util.HashMap;
 import java.util.List;
@@ -64,21 +66,29 @@
  * View visible under the clock on the lock screen and AoD.
  */
 public class KeyguardSliceView extends LinearLayout implements View.OnClickListener,
-        Observer<Slice>, TunerService.Tunable {
+        Observer<Slice>, TunerService.Tunable, ConfigurationController.ConfigurationListener {
 
     private static final String TAG = "KeyguardSliceView";
+    public static final int DEFAULT_ANIM_DURATION = 550;
+
     private final HashMap<View, PendingIntent> mClickActions;
     private Uri mKeyguardSliceUri;
-    private TextView mTitle;
-    private LinearLayout mRow;
+    @VisibleForTesting
+    TextView mTitle;
+    private Row mRow;
     private int mTextColor;
     private float mDarkAmount = 0;
 
     private LiveData<Slice> mLiveData;
     private int mIconSize;
-    private Consumer<Boolean> mListener;
+    /**
+     * Listener called whenever the view contents change.
+     * Boolean will be true when the change happens animated.
+     */
+    private Consumer<Boolean> mContentChangeListener;
     private boolean mHasHeader;
-    private boolean mHideContent;
+    private Slice mSlice;
+    private boolean mPulsing;
 
     public KeyguardSliceView(Context context) {
         this(context, null, 0);
@@ -95,6 +105,18 @@
         tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
 
         mClickActions = new HashMap<>();
+
+        LayoutTransition transition = new LayoutTransition();
+        transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
+        transition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
+        transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
+        transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+        transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+        transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
+        transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
+        transition.setAnimateParentHierarchy(false);
+        transition.addTransitionListener(new SliceViewTransitionListener());
+        setLayoutTransition(transition);
     }
 
     @Override
@@ -112,6 +134,7 @@
 
         // Make sure we always have the most current slice
         mLiveData.observeForever(this);
+        Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
     @Override
@@ -119,17 +142,29 @@
         super.onDetachedFromWindow();
 
         mLiveData.removeObserver(this);
+        Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
-    private void showSlice(Slice slice) {
+    private void showSlice() {
+        if (mPulsing) {
+            mTitle.setVisibility(GONE);
+            mRow.setVisibility(GONE);
+            mContentChangeListener.accept(getLayoutTransition() != null);
+            return;
+        }
 
-        ListContent lc = new ListContent(getContext(), slice);
+        if (mSlice == null) {
+            return;
+        }
+
+        ListContent lc = new ListContent(getContext(), mSlice);
         mHasHeader = lc.hasHeader();
         List<SliceItem> subItems = lc.getRowItems();
         if (!mHasHeader) {
             mTitle.setVisibility(GONE);
         } else {
             mTitle.setVisibility(VISIBLE);
+
             // If there's a header it'll be the first subitem
             RowContent header = new RowContent(getContext(), subItems.get(0),
                     true /* showStartItem */);
@@ -154,6 +189,7 @@
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
         final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
+        mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
         for (int i = startIndex; i < subItemsCount; i++) {
             SliceItem item = subItems.get(i);
             RowContent rc = new RowContent(getContext(), item, true /* showStartItem */);
@@ -200,15 +236,20 @@
             }
         }
 
-        updateVisibility();
-        mListener.accept(mHasHeader);
+        if (mContentChangeListener != null) {
+            mContentChangeListener.accept(getLayoutTransition() != null);
+        }
     }
 
-    private void updateVisibility() {
-        final boolean hasContent = mHasHeader || mRow.getChildCount() > 0;
-        final int visibility = hasContent && !mHideContent ? VISIBLE : GONE;
-        if (visibility != getVisibility()) {
-            setVisibility(visibility);
+    public void setPulsing(boolean pulsing, boolean animate) {
+        mPulsing = pulsing;
+        LayoutTransition transition = getLayoutTransition();
+        if (!animate) {
+            setLayoutTransition(null);
+        }
+        showSlice();
+        if (!animate) {
+            setLayoutTransition(transition);
         }
     }
 
@@ -252,8 +293,9 @@
         return optimalString.toString();
     }
 
-    public void setDark(float darkAmount) {
+    public void setDarkAmount(float darkAmount) {
         mDarkAmount = darkAmount;
+        mRow.setDarkAmount(darkAmount);
         updateTextColors();
     }
 
@@ -281,12 +323,17 @@
         }
     }
 
-    public void setListener(Consumer<Boolean> listener) {
-        mListener = listener;
+    /**
+     * Listener that gets invoked every time the title or the row visibility changes.
+     * Parameter will be {@code true} whenever the change happens animated.
+     * @param contentChangeListener The listener.
+     */
+    public void setContentChangeListener(Consumer<Boolean> contentChangeListener) {
+        mContentChangeListener = contentChangeListener;
     }
 
     public boolean hasHeader() {
-        return mHasHeader;
+        return mTitle.getVisibility() == VISIBLE;
     }
 
     /**
@@ -295,7 +342,8 @@
      */
     @Override
     public void onChanged(Slice slice) {
-        showSlice(slice);
+        mSlice = slice;
+        showSlice();
     }
 
     @Override
@@ -333,15 +381,20 @@
         updateTextColors();
     }
 
-    public void setHideContent(boolean hideContent) {
-        mHideContent = hideContent;
-        updateVisibility();
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size);
     }
 
     public static class Row extends LinearLayout {
 
-        private static final long ROW_ANIM_DURATION = 350;
-        private final WakeLock mAnimationWakeLock;
+        /**
+         * This view is visible in AOD, which means that the device will sleep if we
+         * don't hold a wake lock. We want to enter doze only after all views have reached
+         * their desired positions.
+         */
+        private final Animation.AnimationListener mKeepAwakeListener;
+        private float mDarkAmount;
 
         public Row(Context context) {
             this(context, null);
@@ -357,16 +410,13 @@
 
         public Row(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
             super(context, attrs, defStyleAttr, defStyleRes);
-            mAnimationWakeLock = WakeLock.createPartial(context, "slice animation");
+            mKeepAwakeListener = new KeepAwakeAnimationListener(mContext);
         }
 
         @Override
         protected void onFinishInflate() {
             LayoutTransition transition = new LayoutTransition();
-            transition.setDuration(ROW_ANIM_DURATION);
-            transition.setStagger(LayoutTransition.CHANGING, ROW_ANIM_DURATION);
-            transition.setStagger(LayoutTransition.CHANGE_APPEARING, ROW_ANIM_DURATION);
-            transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, ROW_ANIM_DURATION);
+            transition.setDuration(DEFAULT_ANIM_DURATION);
 
             PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1);
             PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1);
@@ -378,7 +428,8 @@
                     Interpolators.ACCELERATE_DECELERATE);
             transition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
                     Interpolators.ACCELERATE_DECELERATE);
-            transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, ROW_ANIM_DURATION);
+            transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION);
+            transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, DEFAULT_ANIM_DURATION);
 
             ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
             transition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
@@ -386,31 +437,11 @@
 
             ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
             transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
-            transition.setDuration(LayoutTransition.DISAPPEARING, ROW_ANIM_DURATION / 2);
+            transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
             transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
 
             transition.setAnimateParentHierarchy(false);
             setLayoutTransition(transition);
-
-            // This view is visible in AOD, which means that the device will sleep if we
-            // don't hold a wake lock. We want to enter doze only after all views have reached
-            // their desired positions.
-            setLayoutAnimationListener(new Animation.AnimationListener() {
-                @Override
-                public void onAnimationStart(Animation animation) {
-                    mAnimationWakeLock.acquire();
-                }
-
-                @Override
-                public void onAnimationEnd(Animation animation) {
-                    mAnimationWakeLock.release();
-                }
-
-                @Override
-                public void onAnimationRepeat(Animation animation) {
-
-                }
-            });
         }
 
         @Override
@@ -424,23 +455,58 @@
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
+
+        public void setDarkAmount(float darkAmount) {
+            boolean isAwake = darkAmount != 0;
+            boolean wasAwake = mDarkAmount != 0;
+            if (isAwake == wasAwake) {
+                return;
+            }
+            mDarkAmount = darkAmount;
+            setLayoutAnimationListener(isAwake ? null : mKeepAwakeListener);
+        }
+
+        @Override
+        public boolean hasOverlappingRendering() {
+            return false;
+        }
     }
 
     /**
      * Representation of an item that appears under the clock on main keyguard message.
      */
-    private class KeyguardSliceButton extends Button {
+    @VisibleForTesting
+    static class KeyguardSliceButton extends Button implements
+            ConfigurationController.ConfigurationListener {
+        private final Context mContext;
 
         public KeyguardSliceButton(Context context) {
             super(context, null /* attrs */, 0 /* styleAttr */,
                     com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
-            int horizontalPadding = (int) context.getResources()
+            mContext = context;
+            onDensityOrFontScaleChanged();
+            setEllipsize(TruncateAt.END);
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            Dependency.get(ConfigurationController.class).addCallback(this);
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            Dependency.get(ConfigurationController.class).removeCallback(this);
+        }
+
+        @Override
+        public void onDensityOrFontScaleChanged() {
+            int horizontalPadding = (int) mContext.getResources()
                     .getDimension(R.dimen.widget_horizontal_padding);
             setPadding(horizontalPadding / 2, 0, horizontalPadding / 2, 0);
-            setCompoundDrawablePadding((int) context.getResources()
+            setCompoundDrawablePadding((int) mContext.getResources()
                     .getDimension(R.dimen.widget_icon_padding));
-            setMaxLines(1);
-            setEllipsize(TruncateAt.END);
         }
 
         @Override
@@ -465,4 +531,38 @@
             }
         }
     }
+
+    private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
+        @Override
+        public void startTransition(LayoutTransition transition, ViewGroup container, View view,
+                int transitionType) {
+            switch (transitionType) {
+                case  LayoutTransition.APPEARING:
+                    int translation = getResources().getDimensionPixelSize(
+                            R.dimen.pulsing_notification_appear_translation);
+                    view.setTranslationY(translation);
+                    view.animate()
+                            .translationY(0)
+                            .setDuration(DEFAULT_ANIM_DURATION)
+                            .setInterpolator(Interpolators.ALPHA_IN)
+                            .start();
+                    break;
+                case LayoutTransition.DISAPPEARING:
+                    if (view == mTitle) {
+                        // Translate the view to the inverse of its height, so the layout event
+                        // won't misposition it.
+                        LayoutParams params = (LayoutParams) mTitle.getLayoutParams();
+                        int margin = params.topMargin + params.bottomMargin;
+                        mTitle.setTranslationY(-mTitle.getHeight() - margin);
+                    }
+                    break;
+            }
+        }
+
+        @Override
+        public void endTransition(LayoutTransition transition, ViewGroup container, View view,
+                int transitionType) {
+
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index a4a3d14..ff182de3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,12 +16,11 @@
 
 package com.android.keyguard;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityManager;
-import android.app.AlarmManager;
 import android.app.IActivityManager;
 import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.os.Handler;
@@ -37,18 +36,24 @@
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.GridLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextClock;
 import android.widget.TextView;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
 
 import com.google.android.collect.Sets;
 
 import java.util.Locale;
 
-public class KeyguardStatusView extends GridLayout {
+public class KeyguardStatusView extends GridLayout implements
+        ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardStatusView";
     private static final int MARQUEE_DELAY_MS = 2000;
@@ -56,13 +61,11 @@
     private final LockPatternUtils mLockPatternUtils;
     private final IActivityManager mIActivityManager;
     private final float mSmallClockScale;
-    private final float mWidgetPadding;
 
     private TextView mLogoutView;
     private TextClock mClockView;
     private View mClockSeparator;
     private TextView mOwnerInfo;
-    private ViewGroup mClockContainer;
     private KeyguardSliceView mKeyguardSlice;
     private Runnable mPendingMarqueeStart;
     private Handler mHandler;
@@ -71,19 +74,22 @@
     private boolean mPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
+    private float mWidgetPadding;
+    private boolean mAnimateLayout;
+    private int mLastLayoutHeight;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
         @Override
         public void onTimeChanged() {
-            refresh();
+            refreshTime();
         }
 
         @Override
         public void onKeyguardVisibilityChanged(boolean showing) {
             if (showing) {
                 if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
-                refresh();
+                refreshTime();
                 updateOwnerInfo();
                 updateLogoutView();
             }
@@ -101,7 +107,7 @@
 
         @Override
         public void onUserSwitchComplete(int userId) {
-            refresh();
+            refreshFormat();
             updateOwnerInfo();
             updateLogoutView();
         }
@@ -127,7 +133,7 @@
         mHandler = new Handler(Looper.myLooper());
         mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
                 / getResources().getDimension(R.dimen.widget_big_font_size);
-        mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+        onDensityOrFontScaleChanged();
     }
 
     private void setEnableMarquee(boolean enabled) {
@@ -160,7 +166,6 @@
         mLogoutView = findViewById(R.id.logout);
         mLogoutView.setOnClickListener(this::onLogoutClicked);
 
-        mClockContainer = findViewById(R.id.keyguard_clock_container);
         mClockView = findViewById(R.id.clock_view);
         mClockView.setShowCurrentUserTime(true);
         if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
@@ -169,46 +174,111 @@
         mOwnerInfo = findViewById(R.id.owner_info);
         mKeyguardSlice = findViewById(R.id.keyguard_status_area);
         mClockSeparator = findViewById(R.id.clock_separator);
-        mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice, mClockSeparator);
+        mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
         mTextColor = mClockView.getCurrentTextColor();
 
-        mKeyguardSlice.setListener(this::onSliceContentChanged);
-        onSliceContentChanged(mKeyguardSlice.hasHeader());
+        mClockView.addOnLayoutChangeListener(this);
+        mClockSeparator.addOnLayoutChangeListener(this);
+        mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
+        onSliceContentChanged(false /* animated */);
 
         boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
         setEnableMarquee(shouldMarquee);
-        refresh();
+        refreshFormat();
         updateOwnerInfo();
         updateLogoutView();
+        updateDark();
 
         // Disable elegant text height because our fancy colon makes the ymin value huge for no
         // reason.
         mClockView.setElegantTextHeight(false);
     }
 
-    private void onSliceContentChanged(boolean hasHeader) {
-        final boolean smallClock = hasHeader || mPulsing;
-        final float clockScale = smallClock ? mSmallClockScale : 1;
-        float translation = (mClockView.getHeight() - (mClockView.getHeight() * clockScale)) / 2f;
-        if (smallClock) {
-            translation -= mWidgetPadding;
+    private void onSliceContentChanged(boolean animated) {
+        mAnimateLayout = animated;
+        boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
+        float clockScale = smallClock ? mSmallClockScale : 1;
+
+        RelativeLayout.LayoutParams layoutParams =
+                (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+        int height = mClockView.getHeight();
+        layoutParams.bottomMargin = (int) -(height - (clockScale * height));
+        mClockView.setLayoutParams(layoutParams);
+
+        layoutParams = (RelativeLayout.LayoutParams) mClockSeparator.getLayoutParams();
+        layoutParams.topMargin = smallClock ? (int) mWidgetPadding : 0;
+        layoutParams.bottomMargin = layoutParams.topMargin;
+        mClockSeparator.setLayoutParams(layoutParams);
+    }
+
+    /**
+     * Animate clock and its separator when necessary.
+     */
+    @Override
+    public void onLayoutChange(View view, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+        int heightOffset = mPulsing ? 0 : getHeight() - mLastLayoutHeight;
+        boolean hasHeader = mKeyguardSlice.hasHeader();
+        boolean smallClock = hasHeader || mPulsing;
+        long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
+        long delay = smallClock ? 0 : duration / 4;
+
+        if (view == mClockView) {
+            float clockScale = smallClock ? mSmallClockScale : 1;
+            if (mAnimateLayout) {
+                mClockView.setY(oldTop + heightOffset);
+                mClockView.animate().cancel();
+                mClockView.animate()
+                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                        .setDuration(duration)
+                        .setListener(new ClipChildrenAnimationListener())
+                        .setStartDelay(delay)
+                        .y(top)
+                        .scaleX(clockScale)
+                        .scaleY(clockScale)
+                        .start();
+            } else {
+                mClockView.setY(top);
+                mClockView.setScaleX(clockScale);
+                mClockView.setScaleY(clockScale);
+            }
+        } else if (view == mClockSeparator) {
+            boolean hasSeparator = hasHeader && !mPulsing;
+            float alpha = hasSeparator ? 1 : 0;
+            if (mAnimateLayout) {
+                boolean isAwake = mDarkAmount != 0;
+                mClockSeparator.setY(oldTop + heightOffset);
+                mClockSeparator.animate().cancel();
+                mClockSeparator.animate()
+                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                        .setDuration(duration)
+                        .setListener(isAwake ? null : new KeepAwakeAnimationListener(getContext()))
+                        .setStartDelay(delay)
+                        .y(top)
+                        .alpha(alpha)
+                        .start();
+            } else {
+                mClockSeparator.setY(top);
+                mClockSeparator.setAlpha(alpha);
+            }
         }
-        mClockView.setTranslationY(translation);
-        mClockView.setScaleX(clockScale);
-        mClockView.setScaleY(clockScale);
-        mClockSeparator.setVisibility(hasHeader && !mPulsing ? VISIBLE : GONE);
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
-        // Some layouts like burmese have a different margin for the clock
-        MarginLayoutParams layoutParams = (MarginLayoutParams) mClockView.getLayoutParams();
-        layoutParams.bottomMargin = getResources().getDimensionPixelSize(
-                R.dimen.bottom_text_spacing_digital);
-        mClockView.setLayoutParams(layoutParams);
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mClockView.setPivotX(mClockView.getWidth() / 2);
+        mClockView.setPivotY(0);
+        mLastLayoutHeight = getHeight();
+    }
+
+    @Override
+    public void onDensityOrFontScaleChanged() {
+        mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+        if (mClockView != null) {
+            mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                    getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
+        }
         if (mOwnerInfo != null) {
             mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                     getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
@@ -219,17 +289,10 @@
         mClockView.refresh();
     }
 
-    private void refresh() {
+    private void refreshFormat() {
         Patterns.update(mContext);
-        refreshTime();
-    }
-
-    public int getClockBottom() {
-        if (mOwnerInfo != null && mOwnerInfo.getVisibility() == VISIBLE) {
-            return mOwnerInfo.getBottom();
-        } else {
-            return mClockContainer.getBottom();
-        }
+        mClockView.setFormat12Hour(Patterns.clockView12);
+        mClockView.setFormat24Hour(Patterns.clockView24);
     }
 
     public int getLogoutButtonHeight() {
@@ -249,34 +312,8 @@
 
     private void updateOwnerInfo() {
         if (mOwnerInfo == null) return;
-        String ownerInfo = getOwnerInfo();
-        if (!TextUtils.isEmpty(ownerInfo)) {
-            mOwnerInfo.setVisibility(View.VISIBLE);
-            mOwnerInfo.setText(ownerInfo);
-        } else {
-            mOwnerInfo.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
-    }
-
-    private String getOwnerInfo() {
-        String info = null;
-        if (mLockPatternUtils.isDeviceOwnerInfoEnabled()) {
-            // Use the device owner information set by device policy client via
-            // device policy manager.
-            info = mLockPatternUtils.getDeviceOwnerInfo();
-        } else {
+        String info = mLockPatternUtils.getDeviceOwnerInfo();
+        if (info == null) {
             // Use the current user owner information if enabled.
             final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
                     KeyguardUpdateMonitor.getCurrentUser());
@@ -284,7 +321,26 @@
                 info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
             }
         }
-        return info;
+        mOwnerInfo.setText(info);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
+        Dependency.get(ConfigurationController.class).addCallback(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
+        Dependency.get(ConfigurationController.class).removeCallback(this);
+    }
+
+    @Override
+    public void onLocaleListChanged() {
+        refreshFormat();
     }
 
     @Override
@@ -329,32 +385,27 @@
             return;
         }
         mDarkAmount = darkAmount;
-
-        boolean dark = darkAmount == 1;
-        mLogoutView.setAlpha(dark ? 0 : 1);
-        final int N = mClockContainer.getChildCount();
-        for (int i = 0; i < N; i++) {
-            View child = mClockContainer.getChildAt(i);
-            if (mVisibleInDoze.contains(child)) {
-                continue;
-            }
-            child.setAlpha(dark ? 0 : 1);
-        }
-        if (mOwnerInfo != null) {
-            mOwnerInfo.setAlpha(dark ? 0 : 1);
-        }
-
-        final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount);
-        updateDozeVisibleViews();
-        mKeyguardSlice.setDark(darkAmount);
-        mClockView.setTextColor(blendedTextColor);
-        mClockSeparator.setBackgroundTintList(ColorStateList.valueOf(blendedTextColor));
+        updateDark();
     }
 
-    public void setPulsing(boolean pulsing) {
+    private void updateDark() {
+        boolean dark = mDarkAmount == 1;
+        mLogoutView.setAlpha(dark ? 0 : 1);
+        if (mOwnerInfo != null) {
+            boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
+            mOwnerInfo.setVisibility(hasText && mDarkAmount != 1 ? VISIBLE : GONE);
+        }
+
+        final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+        updateDozeVisibleViews();
+        mKeyguardSlice.setDarkAmount(mDarkAmount);
+        mClockView.setTextColor(blendedTextColor);
+        mClockSeparator.setBackgroundColor(blendedTextColor);
+    }
+
+    public void setPulsing(boolean pulsing, boolean animate) {
         mPulsing = pulsing;
-        mKeyguardSlice.setHideContent(pulsing);
-        onSliceContentChanged(mKeyguardSlice.hasHeader());
+        mKeyguardSlice.setPulsing(pulsing, animate);
         updateDozeVisibleViews();
     }
 
@@ -378,4 +429,24 @@
             Log.e(TAG, "Failed to logout user", re);
         }
     }
+
+    private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements
+            ViewClippingUtil.ClippingParameters {
+
+        ClipChildrenAnimationListener() {
+            ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */,
+                    this /* clippingParams */);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */,
+                    this /* clippingParams */);
+        }
+
+        @Override
+        public boolean shouldFinish(View view) {
+            return view == getParent();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d24675c..fb13925 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,8 +29,11 @@
 import static android.os.BatteryManager.EXTRA_PLUGGED;
 import static android.os.BatteryManager.EXTRA_STATUS;
 
+import android.annotation.AnyThread;
+import android.annotation.MainThread;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.UserSwitchObserver;
 import android.app.admin.DevicePolicyManager;
@@ -44,7 +47,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
-import android.graphics.Bitmap;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -77,6 +79,7 @@
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -355,6 +358,7 @@
 
     private static int sCurrentUser;
     private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
+    private static boolean sDisableHandlerCheckForTesting;
 
     public synchronized static void setCurrentUser(int currentUser) {
         sCurrentUser = currentUser;
@@ -366,6 +370,7 @@
 
     @Override
     public void onTrustChanged(boolean enabled, int userId, int flags) {
+        checkIsHandlerThread();
         mUserHasTrust.put(userId, enabled);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -383,7 +388,7 @@
         dispatchErrorMessage(message);
     }
 
-    protected void handleSimSubscriptionInfoChanged() {
+    private void handleSimSubscriptionInfoChanged() {
         if (DEBUG_SIM_STATES) {
             Log.v(TAG, "onSubscriptionInfoChanged()");
             List<SubscriptionInfo> sil = mSubscriptionManager.getActiveSubscriptionInfoList();
@@ -451,6 +456,7 @@
 
     @Override
     public void onTrustManagedChanged(boolean managed, int userId) {
+        checkIsHandlerThread();
         mUserTrustIsManaged.put(userId, managed);
 
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -638,6 +644,7 @@
     }
 
     private void notifyFingerprintRunningStateChanged() {
+        checkIsHandlerThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -646,6 +653,7 @@
         }
     }
     private void handleFaceUnlockStateChanged(boolean running, int userId) {
+        checkIsHandlerThread();
         mUserFaceUnlockRunning.put(userId, running);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -705,6 +713,7 @@
     }
 
     private void notifyStrongAuthStateChanged(int userId) {
+        checkIsHandlerThread();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1125,20 +1134,6 @@
         updateFingerprintListeningState();
     }
 
-    /**
-     * IMPORTANT: Must be called from UI thread.
-     */
-    public void dispatchSetBackground(Bitmap bmp) {
-        if (DEBUG) Log.d(TAG, "dispatchSetBackground");
-        final int count = mCallbacks.size();
-        for (int i = 0; i < count; i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onSetBackground(bmp);
-            }
-        }
-    }
-
     private void handleUserInfoChanged(int userId) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1350,6 +1345,7 @@
      * @param hasLockscreenWallpaper Whether Keyguard has a lockscreen wallpaper.
      */
     public void setHasLockscreenWallpaper(boolean hasLockscreenWallpaper) {
+        checkIsHandlerThread();
         if (hasLockscreenWallpaper != mHasLockscreenWallpaper) {
             mHasLockscreenWallpaper = hasLockscreenWallpaper;
             for (int i = mCallbacks.size() - 1; i >= 0; i--) {
@@ -1371,7 +1367,7 @@
     /**
      * Handle {@link #MSG_DPM_STATE_CHANGED}
      */
-    protected void handleDevicePolicyManagerStateChanged() {
+    private void handleDevicePolicyManagerStateChanged() {
         updateFingerprintListeningState();
         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1384,7 +1380,7 @@
     /**
      * Handle {@link #MSG_USER_SWITCHING}
      */
-    protected void handleUserSwitching(int userId, IRemoteCallback reply) {
+    private void handleUserSwitching(int userId, IRemoteCallback reply) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1400,7 +1396,7 @@
     /**
      * Handle {@link #MSG_USER_SWITCH_COMPLETE}
      */
-    protected void handleUserSwitchComplete(int userId) {
+    private void handleUserSwitchComplete(int userId) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1422,7 +1418,7 @@
     /**
      * Handle {@link #MSG_BOOT_COMPLETED}
      */
-    protected void handleBootCompleted() {
+    private void handleBootCompleted() {
         if (mBootCompleted) return;
         mBootCompleted = true;
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1444,7 +1440,7 @@
     /**
      * Handle {@link #MSG_DEVICE_PROVISIONED}
      */
-    protected void handleDeviceProvisioned() {
+    private void handleDeviceProvisioned() {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1461,7 +1457,7 @@
     /**
      * Handle {@link #MSG_PHONE_STATE_CHANGED}
      */
-    protected void handlePhoneStateChanged(String newState) {
+    private void handlePhoneStateChanged(String newState) {
         if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
         if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) {
             mPhoneState = TelephonyManager.CALL_STATE_IDLE;
@@ -1481,7 +1477,7 @@
     /**
      * Handle {@link #MSG_RINGER_MODE_CHANGED}
      */
-    protected void handleRingerModeChange(int mode) {
+    private void handleRingerModeChange(int mode) {
         if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
         mRingMode = mode;
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1526,8 +1522,8 @@
      * Handle {@link #MSG_SIM_STATE_CHANGE}
      */
     @VisibleForTesting
-    protected void handleSimStateChange(int subId, int slotId, State state) {
-
+    void handleSimStateChange(int subId, int slotId, State state) {
+        checkIsHandlerThread();
         if (DEBUG_SIM_STATES) {
             Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId="
                     + slotId + ", state=" + state +")");
@@ -1590,6 +1586,7 @@
      * <p>Needs to be called from the main thread.
      */
     public void onKeyguardVisibilityChanged(boolean showing) {
+        checkIsHandlerThread();
         if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
         mKeyguardIsVisible = showing;
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1680,6 +1677,7 @@
      * @param callback The callback to remove
      */
     public void removeCallback(KeyguardUpdateMonitorCallback callback) {
+        checkIsHandlerThread();
         if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
             if (mCallbacks.get(i).get() == callback) {
@@ -1694,6 +1692,7 @@
      * @param callback The callback to register
      */
     public void registerCallback(KeyguardUpdateMonitorCallback callback) {
+        checkIsHandlerThread();
         if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
         // Prevent adding duplicate callbacks
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1712,6 +1711,7 @@
         return mSwitchingUser;
     }
 
+    @AnyThread
     public void setSwitchingUser(boolean switching) {
         mSwitchingUser = switching;
         // Since this comes in on a binder thread, we need to post if first
@@ -1755,6 +1755,7 @@
      * NOTE: Because handleSimStateChange() invokes callbacks immediately without going
      * through mHandler, this *must* be called from the UI thread.
      */
+    @MainThread
     public void reportSimUnlocked(int subId) {
         if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")");
         int slotId = SubscriptionManager.getSlotIndex(subId);
@@ -1773,6 +1774,7 @@
         if (!bypassHandler) {
             mHandler.obtainMessage(MSG_REPORT_EMERGENCY_CALL_ACTION).sendToTarget();
         } else {
+            checkIsHandlerThread();
             handleReportEmergencyCallAction();
         }
     }
@@ -1975,6 +1977,7 @@
     }
 
     private void updateLogoutEnabled() {
+        checkIsHandlerThread();
         boolean logoutEnabled = mDevicePolicyManager.isLogoutEnabled();
         if (mLogoutEnabled != logoutEnabled) {
             mLogoutEnabled = logoutEnabled;
@@ -1987,6 +1990,36 @@
         }
     }
 
+    private void checkIsHandlerThread() {
+        if (sDisableHandlerCheckForTesting) {
+            return;
+        }
+        if (!mHandler.getLooper().isCurrentThread()) {
+            Log.wtf(TAG, "must call on mHandler's thread "
+                    + mHandler.getLooper().getThread() + ", not " + Thread.currentThread());
+        }
+    }
+
+    /**
+     * Turn off the handler check for testing.
+     *
+     * This is necessary because currently tests are not too careful about which thread they call
+     * into this class on.
+     *
+     * Note that this must be called before scheduling any work involving KeyguardUpdateMonitor
+     * instances.
+     *
+     * TODO: fix the tests and remove this.
+     */
+    @VisibleForTesting
+    public static void disableHandlerCheckForTesting(Instrumentation instrumentation) {
+        Preconditions.checkNotNull(instrumentation, "Must only call this method in tests!");
+        // Don't need synchronization here *if* the callers follow the contract and call this only
+        // before scheduling work for KeyguardUpdateMonitor on other threads, because the scheduling
+        // of that work forces a happens-before relationship.
+        sDisableHandlerCheckForTesting = true;
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardUpdateMonitor state:");
         pw.println("  SIM States:");
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 913e781..a265a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -28,6 +28,7 @@
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityManager;
 
@@ -74,6 +75,7 @@
     private boolean mEnforceBouncer = false;
     private boolean mBouncerOn = false;
     private boolean mSessionActive = false;
+    private boolean mIsTouchScreen = true;
     private int mState = StatusBarState.SHADE;
     private boolean mScreenOn;
     private boolean mShowingAod;
@@ -236,6 +238,11 @@
             // already sufficiently prevents false unlocks.
             return false;
         }
+        if (!mIsTouchScreen) {
+            // Unlocking with input devices besides the touchscreen should already be sufficiently
+            // anti-falsed.
+            return false;
+        }
         return mHumanInteractionClassifier.isFalseTouch();
     }
 
@@ -450,6 +457,9 @@
     }
 
     public void onTouchEvent(MotionEvent event, int width, int height) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+        }
         if (mSessionActive && !mBouncerOn) {
             mDataCollector.onTouchEvent(event, width, height);
             mHumanInteractionClassifier.onTouchEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index a0fdb9d..90140ff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -36,7 +36,13 @@
      * Delay entering low power mode when animating to make sure that we'll have
      * time to move all elements into their final positions while still at 60 fps.
      */
-    private static final int ENTER_DOZE_DELAY = 3000;
+    private static final int ENTER_DOZE_DELAY = 6000;
+    /**
+     * Hide wallpaper earlier when entering low power mode. The gap between
+     * hiding the wallpaper and changing the display mode is necessary to hide
+     * the black frame that's inherent to hardware specs.
+     */
+    public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 2000;
 
     private final DozeMachine.Service mDozeService;
     private final Handler mHandler;
@@ -74,7 +80,9 @@
         }
 
         boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
-        if (messagePending || oldState == DozeMachine.State.INITIALIZED) {
+        boolean pulseEnding = oldState  == DozeMachine.State.DOZE_PULSE_DONE
+                && newState == DozeMachine.State.DOZE_AOD;
+        if (messagePending || oldState == DozeMachine.State.INITIALIZED || pulseEnding) {
             // During initialization, we hide the navigation bar. That is however only applied after
             // a traversal; setting the screen state here is immediate however, so it can happen
             // that the screen turns on again before the navigation bar is hidden. To work around
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a7975d7..087d481 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1380,6 +1380,15 @@
             mHardwareLayout = HardwareUiLayout.get(mListView);
             mHardwareLayout.setOutsideTouchListener(view -> dismiss());
             setTitle(R.string.global_actions);
+            mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+                @Override
+                public boolean dispatchPopulateAccessibilityEvent(
+                        View host, AccessibilityEvent event) {
+                    // Populate the title here, just as Activity does
+                    event.getText().add(mContext.getString(R.string.global_actions));
+                    return true;
+                }
+            });
         }
 
         private void updateList() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ca92d35..6809e76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -165,6 +165,7 @@
     private static final int NOTIFY_SCREEN_TURNED_ON = 15;
     private static final int NOTIFY_SCREEN_TURNED_OFF = 16;
     private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17;
+    private static final int SYSTEM_READY = 18;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -775,6 +776,10 @@
      * Let us know that the system is ready after startup.
      */
     public void onSystemReady() {
+        mHandler.obtainMessage(SYSTEM_READY).sendToTarget();
+    }
+
+    private void handleSystemReady() {
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "onSystemReady");
             mSystemReady = true;
@@ -1603,6 +1608,9 @@
                     Log.w(TAG, "Timeout while waiting for activity drawn!");
                     Trace.endSection();
                     break;
+                case SYSTEM_READY:
+                    handleSystemReady();
+                    break;
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index a25c466..cd00311 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -124,8 +124,9 @@
         final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
         final boolean enabled = transientEnabling || mController.isBluetoothEnabled();
         final boolean connected = mController.isBluetoothConnected();
-        state.isTransient = transientEnabling || mController.isBluetoothConnecting()
-                || mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
+        final boolean connecting = mController.isBluetoothConnecting();
+        state.isTransient = transientEnabling || connecting ||
+                mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
         state.dualTarget = true;
         state.value = enabled;
         if (state.slash == null) {
@@ -134,7 +135,7 @@
         state.slash.isSlashed = !enabled;
         state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
         state.secondaryLabel = TextUtils.emptyIfNull(
-                getSecondaryLabel(enabled, connected, state.isTransient));
+                getSecondaryLabel(enabled, connecting, connected, state.isTransient));
         if (enabled) {
             if (connected) {
                 state.icon = new BluetoothConnectedTileIcon();
@@ -170,13 +171,17 @@
      * Returns the secondary label to use for the given bluetooth connection in the form of the
      * battery level or bluetooth profile name. If the bluetooth is disabled, there's no connected
      * devices, or we can't map the bluetooth class to a profile, this instead returns {@code null}.
-     *
      * @param enabled whether bluetooth is enabled
+     * @param connecting whether bluetooth is connecting to a device
      * @param connected whether there's a device connected via bluetooth
      * @param isTransient whether bluetooth is currently in a transient state turning on
      */
     @Nullable
-    private String getSecondaryLabel(boolean enabled, boolean connected, boolean isTransient) {
+    private String getSecondaryLabel(boolean enabled, boolean connecting, boolean connected,
+            boolean isTransient) {
+        if (connecting) {
+            return mContext.getString(R.string.quick_settings_connecting);
+        }
         if (isTransient) {
             return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1149ad1..79fea9f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -21,6 +21,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -364,14 +366,7 @@
         if (mStableInsets.isEmpty()) {
             SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
         }
-        int position = (int) (mState.mRatioPositionBeforeMinimized *
-                (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
-        mSnapAlgorithm = null;
-        initializeSnapAlgorithm();
-
-        // Set the snap target before minimized but do not save until divider is attached and not
-        // minimized because it does not know its minimized state yet.
-        mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+        repositionSnapTargetBeforeMinimized();
     }
 
     public WindowManagerProxy getWindowManagerProxy() {
@@ -878,9 +873,36 @@
     }
 
     public void notifyDockSideChanged(int newDockSide) {
+        int oldDockSide = mDockSide;
         mDockSide = newDockSide;
         mMinimizedShadow.setDockSide(mDockSide);
         requestLayout();
+
+        // Update the snap position to the new docked side with correct insets
+        SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+        mMinimizedSnapAlgorithm = null;
+        initializeSnapAlgorithm();
+
+        if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
+                || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
+            repositionSnapTargetBeforeMinimized();
+        }
+
+        // Landscape to seascape rotation requires minimized to resize docked app correctly
+        if (mHomeStackResizable && mDockedStackMinimized) {
+            resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+        }
+    }
+
+    private void repositionSnapTargetBeforeMinimized() {
+        int position = (int) (mState.mRatioPositionBeforeMinimized *
+                (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
+        mSnapAlgorithm = null;
+        initializeSnapAlgorithm();
+
+        // Set the snap target before minimized but do not save until divider is attached and not
+        // minimized because it does not know its minimized state yet.
+        mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
     }
 
     private void updateDisplayInfo() {
@@ -962,6 +984,12 @@
         if (mHomeStackResizable && mIsInMinimizeInteraction) {
             calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
                     mDockedTaskRect);
+
+            // Move a right-docked-app to line up with the divider while dragging it
+            if (mDockSide == DOCKED_RIGHT) {
+                mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
+                        - mDockedTaskRect.left + mDividerSize, 0);
+            }
             calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
                     DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
@@ -976,6 +1004,12 @@
                 calculateBoundsForPosition(isHorizontalDivision() ? mDisplayHeight : mDisplayWidth,
                         mDockSide, mDockedTaskRect);
             }
+
+            // Move a docked app if from the right in position with the divider up to insets
+            if (mDockSide == DOCKED_RIGHT) {
+                mDockedTaskRect.offset(Math.max(position,
+                        mStableInsets.left) - mDockedTaskRect.left, 0);
+            }
             calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
                     mOtherTaskRect);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java
new file mode 100644
index 0000000..742005d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * A TextView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaOptimizedTextView extends TextView {
+    public AlphaOptimizedTextView(Context context) {
+        super(context);
+    }
+
+    public AlphaOptimizedTextView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AlphaOptimizedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AlphaOptimizedTextView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 44136c5..d3caf03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -407,13 +407,14 @@
         boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
+            final ViewTreeObserver observer = icon.getViewTreeObserver();
             ViewTreeObserver.OnPreDrawListener predrawListener =
                     new ViewTreeObserver.OnPreDrawListener() {
                         @Override
                         public boolean onPreDraw() {
                             boolean animatingY = ViewState.isAnimatingY(icon);
-                            if (!animatingY || !icon.isAttachedToWindow()) {
-                                icon.getViewTreeObserver().removeOnPreDrawListener(this);
+                            if (!animatingY) {
+                                observer.removeOnPreDrawListener(this);
                                 icon.setTag(TAG_CONTINUOUS_CLIPPING, null);
                                 return true;
                             }
@@ -421,7 +422,20 @@
                             return true;
                         }
                     };
-            icon.getViewTreeObserver().addOnPreDrawListener(predrawListener);
+            observer.addOnPreDrawListener(predrawListener);
+            icon.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    if (v == icon) {
+                        observer.removeOnPreDrawListener(predrawListener);
+                        icon.setTag(TAG_CONTINUOUS_CLIPPING, null);
+                    }
+                }
+            });
             icon.setTag(TAG_CONTINUOUS_CLIPPING, predrawListener);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index a950cd5..a54c778 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -207,7 +207,7 @@
 
                 // If the user selects Guest, start the guest session.
                 if (userRecord.mIsStartGuestSession) {
-                    mUserManagerHelper.switchToGuest(mGuestName);
+                    mUserManagerHelper.startNewGuestSession(mGuestName);
                     return;
                 }
 
@@ -241,8 +241,7 @@
 
         private Bitmap getUserRecordIcon(UserRecord userRecord) {
             if (userRecord.mIsStartGuestSession) {
-                return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
-                    mContext.getResources(), UserHandle.USER_NULL, false));
+                return mUserManagerHelper.getGuestDefaultIcon();
             }
 
             if (userRecord.mIsAddUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index d2d9c4c..85fac16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -30,6 +30,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
+import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.tuner.TunerService;
 
 import java.io.PrintWriter;
@@ -153,7 +154,8 @@
      * @return duration in millis.
      */
     public long getWallpaperAodDuration() {
-        return mAlwaysOnPolicy.wallpaperVisibilityDuration;
+        return shouldControlScreenOff() ? DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY
+                : mAlwaysOnPolicy.wallpaperVisibilityDuration;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index f6ec493..c4570db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -281,6 +281,7 @@
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
         notifyNavigationBarScreenOn();
         mOverviewProxyService.addCallback(mOverviewProxyListener);
@@ -521,7 +522,7 @@
 
         // Clear any pending suggestion flag as it has either been nullified or is being shown
         mPendingRotationSuggestion = false;
-        getView().removeCallbacks(mCancelPendingRotationProposal);
+        if (getView() != null) getView().removeCallbacks(mCancelPendingRotationProposal);
 
         // Handle the visibility change and animation
         if (visible) { // Appear and change (cannot force)
@@ -1084,6 +1085,10 @@
                     || Intent.ACTION_SCREEN_ON.equals(action)) {
                 notifyNavigationBarScreenOn();
             }
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                // The accessibility settings may be different for the new user
+                updateAccessibilityServicesState(mAccessibilityManager);
+            };
         }
     };
 
@@ -1141,6 +1146,7 @@
         private final Runnable mRipple = new Runnable() {
             @Override
             public void run() { // Cause the ripple to fire via false presses
+                if (!mRoot.isAttachedToWindow()) return;
                 mRoot.setPressed(true);
                 mRoot.setPressed(false);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a0a97c5..b650944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -392,6 +392,8 @@
         if (mQs != null) {
             mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
             mQsMaxExpansionHeight = mQs.getDesiredHeight();
+            mNotificationStackScroller.setMaxTopPadding(
+                    mQsMaxExpansionHeight + mQsNotificationTopPadding);
         }
         positionClockAndNotifications();
         if (mQsExpanded && mQsFullyExpanded) {
@@ -588,6 +590,19 @@
         mNotificationStackScroller.resetScrollPosition();
     }
 
+    @Override
+    public void collapse(boolean delayed, float speedUpFactor) {
+        if (!canPanelBeCollapsed()) {
+            return;
+        }
+
+        if (mQsExpanded) {
+            mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
+        }
+        super.collapse(delayed, speedUpFactor);
+    }
+
     public void closeQs() {
         cancelQsAnimation();
         setQsExpansion(mQsMinExpansionHeight);
@@ -615,6 +630,7 @@
     public void expandWithQs() {
         if (mQsExpansionEnabled) {
             mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
         }
         expand(true /* animate */);
     }
@@ -878,6 +894,7 @@
                 && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
             MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1);
             mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
             requestPanelHeightUpdate();
 
             // Normally, we start listening when the panel is expanded, but here we need to start
@@ -1321,7 +1338,9 @@
 
     protected void updateQsExpansion() {
         if (mQs == null) return;
-        mQs.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
+        float qsExpansionFraction = getQsExpansionFraction();
+        mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
+        mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
     }
 
     private String determineAccessibilityPaneTitle() {
@@ -1357,7 +1376,6 @@
         } else if (mQsSizeChangeAnimator != null) {
             return (int) mQsSizeChangeAnimator.getAnimatedValue();
         } else if (mKeyguardShowing) {
-
             // We can only do the smoother transition on Keyguard when we also are not collapsing
             // from a scrolled quick settings.
             return interpolate(getQsExpansionFraction(),
@@ -1527,7 +1545,6 @@
                 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
                 t = expandedHeight / (getMaxPanelHeight());
             } else {
-
                 // In Shade, interpolate linearly such that QS is closed whenever panel height is
                 // minimum QS expansion + minStackHeight
                 float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
@@ -1776,6 +1793,7 @@
             setListening(true);
         }
         mQsExpandImmediate = false;
+        mNotificationStackScroller.setShouldShowShelfOnly(false);
         mTwoFingerQsExpandPossible = false;
         mIsExpansionFromHeadsUp = false;
         notifyListenersTrackingHeadsUp(null);
@@ -1827,6 +1845,7 @@
         super.onTrackingStarted();
         if (mQsFullyExpanded) {
             mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
         }
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
                 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
@@ -1866,7 +1885,7 @@
         if (view == null && mQsExpanded) {
             return;
         }
-        if (needsAnimation) {
+        if (needsAnimation && mDarkAmount == 0) {
             mAnimateNextPositionUpdate = true;
         }
         ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
@@ -1894,6 +1913,8 @@
         if (mAccessibilityManager.isEnabled()) {
             setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
         }
+        mNotificationStackScroller.setMaxTopPadding(
+                mQsMaxExpansionHeight + mQsNotificationTopPadding);
     }
 
     @Override
@@ -2652,10 +2673,13 @@
 
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
-        mKeyguardStatusView.setPulsing(pulsing);
-        positionClockAndNotifications();
-        mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
-                + mKeyguardStatusView.getClockBottom());
+        final boolean canAnimatePulse =
+                !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
+        if (canAnimatePulse) {
+            mAnimateNextPositionUpdate = true;
+        }
+        mNotificationStackScroller.setPulsing(pulsing, canAnimatePulse);
+        mKeyguardStatusView.setPulsing(pulsing, canAnimatePulse);
     }
 
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 762fb05..5ae8529 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -34,7 +34,6 @@
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.WindowManagerGlobal;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -45,16 +44,13 @@
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.NavigationBarCompat;
 
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
 import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
 import static com.android.systemui.OverviewProxyService.TAG_OPS;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_DRAG_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_TOUCH_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_DRAG_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_TOUCH_SLOP_PX;
 
 /**
  * Class to detect gestures on the navigation bar and implement quick scrub.
@@ -215,17 +211,23 @@
                 int pos, touchDown, offset, trackSize;
 
                 if (mIsVertical) {
-                    exceededScrubTouchSlop = yDiff > QUICK_SCRUB_TOUCH_SLOP_PX && yDiff > xDiff;
-                    exceededSwipeUpTouchSlop = xDiff > QUICK_STEP_TOUCH_SLOP_PX && xDiff > yDiff;
-                    exceededScrubDragSlop = yDiff > QUICK_SCRUB_DRAG_SLOP_PX && yDiff > xDiff;
+                    exceededScrubTouchSlop =
+                            yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
+                    exceededSwipeUpTouchSlop =
+                            xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
+                    exceededScrubDragSlop =
+                            yDiff > NavigationBarCompat.getQuickScrubDragSlopPx() && yDiff > xDiff;
                     pos = y;
                     touchDown = mTouchDownY;
                     offset = pos - mTrackRect.top;
                     trackSize = mTrackRect.height();
                 } else {
-                    exceededScrubTouchSlop = xDiff > QUICK_SCRUB_TOUCH_SLOP_PX && xDiff > yDiff;
-                    exceededSwipeUpTouchSlop = yDiff > QUICK_STEP_TOUCH_SLOP_PX && yDiff > xDiff;
-                    exceededScrubDragSlop = xDiff > QUICK_SCRUB_DRAG_SLOP_PX && xDiff > yDiff;
+                    exceededScrubTouchSlop =
+                            xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
+                    exceededSwipeUpTouchSlop =
+                            yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
+                    exceededScrubDragSlop =
+                            xDiff > NavigationBarCompat.getQuickScrubDragSlopPx() && xDiff > yDiff;
                     pos = x;
                     touchDown = mTouchDownX;
                     offset = pos - mTrackRect.left;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1d64088..ff0adb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -24,6 +24,8 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager
         .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -178,6 +180,7 @@
 import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
 import com.android.systemui.recents.events.activity.UndockingTaskEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -207,14 +210,12 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -1339,8 +1340,15 @@
         }
         int dockSide = WindowManagerProxy.getInstance().getDockSide();
         if (dockSide == WindowManager.DOCKED_INVALID) {
-            return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
-                    ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
+            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+            if (navbarPos == NAV_BAR_POS_INVALID) {
+                return false;
+            }
+            int createMode = navbarPos == NAV_BAR_POS_LEFT
+                    ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
+                    : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+            return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,
+                    null, metricsDockAction);
         } else {
             Divider divider = getComponent(Divider.class);
             if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
@@ -1896,6 +1904,13 @@
             }
         }
 
+        if (!panelsEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey());
+            }
+            return false;
+        }
+
         if (sbn.getNotification().fullScreenIntent != null) {
             if (mAccessibilityManager.isTouchExplorationEnabled()) {
                 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index b4e7575..24a5896 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -88,7 +88,7 @@
         List<Slot> allSlots = getSlots();
         for (int i = 0; i < allSlots.size(); i++) {
             Slot slot = allSlots.get(i);
-            List<StatusBarIconHolder> holders = slot.getHolderList();
+            List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
             boolean blocked = mIconBlacklist.contains(slot.getName());
 
             for (StatusBarIconHolder holder : holders) {
@@ -121,7 +121,7 @@
         // Remove all the icons.
         for (int i = currentSlots.size() - 1; i >= 0; i--) {
             Slot s = currentSlots.get(i);
-            slotsToReAdd.put(s, s.getHolderList());
+            slotsToReAdd.put(s, s.getHolderListInViewOrder());
             removeAllIconsForSlot(s.getName());
         }
 
@@ -281,7 +281,7 @@
         mIconLogger.onIconHidden(slotName);
 
         int slotIndex = getSlotIndex(slotName);
-        List<StatusBarIconHolder> iconsToRemove = slot.getHolderList();
+        List<StatusBarIconHolder> iconsToRemove = slot.getHolderListInViewOrder();
         for (StatusBarIconHolder holder : iconsToRemove) {
             int viewIndex = getViewIndex(slotIndex, holder.getTag());
             slot.removeForTag(holder.getTag());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index c773170..b7e1cfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -211,7 +211,7 @@
         }
 
         /**
-         * View index is backwards from regular index
+         * View index is inverted from regular index, because they are laid out back-to-front
          * @param tag the tag of the holder being viewed
          * @return (1 + mSubSlots.size() - indexOfTag)
          */
@@ -228,14 +228,22 @@
             return subSlots - getIndexForTag(tag) - 1;
         }
 
-        public List<StatusBarIconHolder> getHolderList() {
+        /**
+         * Build a list of the {@link StatusBarIconHolder}s in the same order they appear in their
+         * view group. This provides a safe list that can be iterated and inserted into its group.
+         *
+         * @return all holders contained here, in view order
+         */
+        public List<StatusBarIconHolder> getHolderListInViewOrder() {
             ArrayList<StatusBarIconHolder> holders = new ArrayList<>();
-            if (mHolder != null) {
-                holders.add(mHolder);
+            if (mSubSlots != null) {
+                for (int i = mSubSlots.size() - 1; i >= 0; i--) {
+                    holders.add(mSubSlots.get(i));
+                }
             }
 
-            if (mSubSlots != null) {
-                holders.addAll(mSubSlots);
+            if (mHolder != null) {
+                holders.add(mHolder);
             }
 
             return holders;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 44e87ff..8df51db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -234,6 +234,7 @@
         mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
                 || bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
         mState = bluetoothState;
+        updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 22a48f0..85cfde8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -49,12 +49,11 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.NavigationBarCompat;
 
 import static android.view.KeyEvent.KEYCODE_HOME;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_TOUCH_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_TOUCH_SLOP_PX;
 
 public class KeyButtonView extends ImageView implements ButtonInterface {
     private static final String TAG = KeyButtonView.class.getSimpleName();
@@ -234,10 +233,12 @@
                 x = (int)ev.getRawX();
                 y = (int)ev.getRawY();
 
-                boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) >
-                        (mIsVertical ? QUICK_SCRUB_TOUCH_SLOP_PX : QUICK_STEP_TOUCH_SLOP_PX);
-                boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) >
-                        (mIsVertical ? QUICK_STEP_TOUCH_SLOP_PX : QUICK_SCRUB_TOUCH_SLOP_PX);
+                boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical
+                        ? NavigationBarCompat.getQuickScrubTouchSlopPx()
+                        : NavigationBarCompat.getQuickStepTouchSlopPx());
+                boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical
+                        ? NavigationBarCompat.getQuickStepTouchSlopPx()
+                        : NavigationBarCompat.getQuickScrubTouchSlopPx());
                 if (exceededTouchSlopX || exceededTouchSlopY) {
                     // When quick step is enabled, prevent animating the ripple triggered by
                     // setPressed and decide to run it on touch up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 897d13b..1798dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -69,6 +69,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardSliceView;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.ExpandHelper;
@@ -168,6 +169,7 @@
     private int mCollapsedSize;
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
+    private int mMaxTopPadding;
     private int mRegularTopPadding;
     private int mDarkTopPadding;
     // Current padding, will be either mRegularTopPadding or mDarkTopPadding
@@ -176,6 +178,7 @@
     private int mDarkSeparatorPadding;
     private int mBottomMargin;
     private int mBottomInset = 0;
+    private float mQsExpansionFraction;
 
     /**
      * The algorithm which calculates the properties for our children
@@ -229,6 +232,7 @@
     private boolean mPanelTracking;
     private boolean mExpandingNotification;
     private boolean mExpandedInThisMotion;
+    private boolean mShouldShowShelfOnly;
     protected boolean mScrollingEnabled;
     protected FooterView mFooterView;
     protected EmptyShadeView mEmptyShadeView;
@@ -370,6 +374,7 @@
     private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
+    private View mNeedingPulseAnimation;
     private float mDarkAmount = 0f;
     private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
             new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
@@ -406,7 +411,6 @@
     private final int mSeparatorWidth;
     private final int mSeparatorThickness;
     private final Rect mBackgroundAnimationRect = new Rect();
-    private int mClockBottom;
     private int mAntiBurnInOffsetX;
     private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
     private int mHeadsUpInset;
@@ -717,11 +721,7 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
-        if (mPulsing) {
-            mTopPadding = mClockBottom;
-        } else {
-            mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
-        }
+        mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
@@ -886,7 +886,20 @@
         float appearFraction = 1.0f;
         if (height >= appearEndPosition) {
             translationY = 0;
-            stackHeight = (int) height;
+            if (mShouldShowShelfOnly) {
+                stackHeight = mTopPadding + mShelf.getIntrinsicHeight();
+            } else if (mQsExpanded) {
+                int stackStartPosition = mContentHeight - mTopPadding + mIntrinsicPadding;
+                int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
+                if (stackStartPosition <= stackEndPosition) {
+                    stackHeight = stackEndPosition;
+                } else {
+                    stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
+                            stackEndPosition, mQsExpansionFraction);
+                }
+            } else {
+                stackHeight = (int) height;
+            }
         } else {
             appearFraction = getAppearFraction(height);
             if (appearFraction >= 0) {
@@ -2584,6 +2597,10 @@
         setExpandedHeight(mExpandedHeight);
     }
 
+    public void setMaxTopPadding(int maxTopPadding) {
+        mMaxTopPadding = maxTopPadding;
+    }
+
     public int getLayoutMinHeight() {
         if (isHeadsUpTransition()) {
             return getTopHeadsUpPinnedHeight();
@@ -3174,6 +3191,7 @@
         generateViewResizeEvent();
         generateGroupExpansionEvent();
         generateAnimateEverythingEvent();
+        generatePulsingAnimationEvent();
         mNeedsAnimation = false;
     }
 
@@ -4428,18 +4446,28 @@
         return mIsExpanded;
     }
 
-    public void setPulsing(boolean pulsing, int clockBottom) {
+    public void setPulsing(boolean pulsing, boolean animated) {
         if (!mPulsing && !pulsing) {
             return;
         }
         mPulsing = pulsing;
-        mClockBottom = clockBottom;
+        mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
         mAmbientState.setPulsing(pulsing);
         updateNotificationAnimationStates();
         updateAlgorithmHeightAndPadding();
         updateContentHeight();
-        notifyHeightChangeListener(mShelf);
         requestChildrenUpdate();
+        notifyHeightChangeListener(null, animated);
+        mNeedsAnimation |= animated;
+    }
+
+    private void generatePulsingAnimationEvent() {
+        if (mNeedingPulseAnimation != null) {
+            int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
+                    : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
+            mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
+            mNeedingPulseAnimation = null;
+        }
     }
 
     public void setFadingOut(boolean fadingOut) {
@@ -4476,6 +4504,10 @@
         updateAlgorithmLayoutMinHeight();
     }
 
+    public void setQsExpansionFraction(float qsExpansionFraction) {
+        mQsExpansionFraction = qsExpansionFraction;
+    }
+
     public void setOwnScrollY(int ownScrollY) {
         if (ownScrollY != mOwnScrollY) {
             // We still want to call the normal scrolled changed for accessibility reasons
@@ -4511,6 +4543,11 @@
         }
     }
 
+    public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
+        mShouldShowShelfOnly =  shouldShowShelfOnly;
+        updateAlgorithmLayoutMinHeight();
+    }
+
     public int getMinExpansionHeight() {
         return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
     }
@@ -4566,7 +4603,8 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
-                        + " alpha:%f scrollY:%d]",
+                        + " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
+                        + " qsExpandFraction=%f]",
                 this.getClass().getSimpleName(),
                 mPulsing ? "T":"f",
                 mAmbientState.isQsCustomizerShowing() ? "T":"f",
@@ -4574,7 +4612,10 @@
                         : getVisibility() == View.GONE ? "gone"
                                 : "invisible",
                 getAlpha(),
-                mAmbientState.getScrollY()));
+                mAmbientState.getScrollY(),
+                mMaxTopPadding,
+                mShouldShowShelfOnly ? "T":"f",
+                mQsExpansionFraction));
     }
 
     public boolean isFullyDark() {
@@ -5007,6 +5048,16 @@
                         .animateTopInset()
                         .animateY()
                         .animateZ(),
+
+                // ANIMATION_TYPE_PULSE_APPEAR
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateY(),
+
+                // ANIMATION_TYPE_PULSE_DISAPPEAR
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateY(),
         };
 
         static int[] LENGTHS = new int[] {
@@ -5067,6 +5118,12 @@
 
                 // ANIMATION_TYPE_EVERYTHING
                 StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_PULSE_APPEAR
+                KeyguardSliceView.DEFAULT_ANIM_DURATION,
+
+                // ANIMATION_TYPE_PULSE_DISAPPEAR
+                KeyguardSliceView.DEFAULT_ANIM_DURATION / 2,
         };
 
         static final int ANIMATION_TYPE_ADD = 0;
@@ -5088,6 +5145,8 @@
         static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
         static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
         static final int ANIMATION_TYPE_EVERYTHING = 18;
+        static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
+        static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
 
         static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
         static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index c80bdc6..d01db14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -60,6 +60,7 @@
     public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
 
     private final int mGoToFullShadeAppearingTranslation;
+    private final int mPulsingAppearingTranslation;
     private final ExpandableViewState mTmpState = new ExpandableViewState();
     private final AnimationProperties mAnimationProperties;
     public NotificationStackScrollLayout mHostLayout;
@@ -90,6 +91,9 @@
         mGoToFullShadeAppearingTranslation =
                 hostLayout.getContext().getResources().getDimensionPixelSize(
                         R.dimen.go_to_full_shade_appearing_translation);
+        mPulsingAppearingTranslation =
+                hostLayout.getContext().getResources().getDimensionPixelSize(
+                        R.dimen.pulsing_notification_appear_translation);
         mAnimationProperties = new AnimationProperties() {
             @Override
             public AnimationFilter getAnimationFilter() {
@@ -427,6 +431,18 @@
                 ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
                 row.prepareExpansionChanged(finalState);
             } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
+                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                mTmpState.copyFrom(viewState);
+                mTmpState.yTranslation += mPulsingAppearingTranslation;
+                mTmpState.alpha = 0;
+                mTmpState.applyToView(changingView);
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
+                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                viewState.yTranslation += mPulsingAppearingTranslation;
+                viewState.alpha = 0;
+            } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
                 // This item is added, initialize it's properties.
                 ExpandableViewState viewState = finalState.getViewStateForView(changingView);
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java
new file mode 100644
index 0000000..a54b0e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.util.wakelock;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.view.animation.Animation;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.util.Assert;
+
+public class KeepAwakeAnimationListener extends AnimatorListenerAdapter
+        implements Animation.AnimationListener {
+    @VisibleForTesting
+    static WakeLock sWakeLock;
+
+    public KeepAwakeAnimationListener(Context context) {
+        Assert.isMainThread();
+        if (sWakeLock == null) {
+            sWakeLock = WakeLock.createPartial(context, "animation");
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animation animation) {
+        onStart();
+    }
+
+    @Override
+    public void onAnimationEnd(Animation animation) {
+        onEnd();
+    }
+
+    @Override
+    public void onAnimationRepeat(Animation animation) {
+
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+        onStart();
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        onEnd();
+    }
+
+    private void onStart() {
+        Assert.isMainThread();
+        sWakeLock.acquire();
+    }
+
+    private void onEnd() {
+        Assert.isMainThread();
+        sWakeLock.release();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 64abfe2..f1a7183 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.volume;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
 import android.annotation.Nullable;
 import android.app.Dialog;
 import android.app.KeyguardManager;
@@ -24,6 +27,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Debug;
@@ -80,7 +84,6 @@
 
     private Window mWindow;
     private CustomDialog mDialog;
-    private ViewGroup mDialogView;
     private PagedListView mListView;
     private ListItemAdapter mPagedListAdapter;
     private final List<ListItem> mVolumeLineItems = new ArrayList<>();
@@ -99,29 +102,6 @@
     private boolean mHovering = false;
     private boolean mExpanded;
 
-    private final View.OnClickListener mSupplementalIconListener = v -> {
-        mExpanded = !mExpanded;
-        if (mExpanded) {
-            for (VolumeRow row : mRows) {
-                // Adding the items which are not coming from default stream.
-                if (!row.defaultStream) {
-                    addSeekbarListItem(row, null);
-                }
-            }
-        } else {
-            // Only keeping the default stream if it is not expended.
-            Iterator itr = mVolumeLineItems.iterator();
-            while (itr.hasNext()) {
-                SeekbarListItem item = (SeekbarListItem) itr.next();
-                VolumeRow row = findRow(item);
-                if (!row.defaultStream) {
-                    itr.remove();
-                }
-            }
-        }
-        mPagedListAdapter.notifyDataSetChanged();
-    };
-
     public CarVolumeDialogImpl(Context context) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mController = Dependency.get(VolumeDialogController.class);
@@ -175,31 +155,31 @@
         mDialog.setCanceledOnTouchOutside(true);
         mDialog.setContentView(R.layout.car_volume_dialog);
         mDialog.setOnShowListener(dialog -> {
-            mDialogView.setTranslationY(-mDialogView.getHeight());
-            mDialogView.setAlpha(0);
-            mDialogView.animate()
+            mListView.setTranslationY(-mListView.getHeight());
+            mListView.setAlpha(0);
+            mListView.animate()
                 .alpha(1)
                 .translationY(0)
                 .setDuration(300)
                 .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                 .start();
         });
-        mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
-        mDialogView.setOnHoverListener((v, event) -> {
+        mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
+        mListView.setOnHoverListener((v, event) -> {
             int action = event.getActionMasked();
             mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
                 || (action == MotionEvent.ACTION_HOVER_MOVE);
             rescheduleTimeoutH();
             return true;
         });
-        mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
 
-        // TODO: apply tint to the supplement icon.
-        addSeekbarListItem(addVolumeRow(AudioManager.STREAM_MUSIC, R.drawable.ic_volume_media,
-            R.drawable.car_ic_arrow_drop_up, true, true), mSupplementalIconListener);
-        addVolumeRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer, 0,
+        addSeekbarListItem(addVolumeRow(AudioManager.STREAM_MUSIC, R.drawable.car_ic_music,
+            R.drawable.car_ic_keyboard_arrow_down, true, true),
+            new ExpandIconListener());
+        // We map AudioManager.STREAM_RING to a navigation icon for demo.
+        addVolumeRow(AudioManager.STREAM_RING, R.drawable.car_ic_navigation, 0,
             true, false);
-        addVolumeRow(AudioManager.STREAM_ALARM, R.drawable.ic_volume_alarm, 0,
+        addVolumeRow(AudioManager.STREAM_NOTIFICATION, R.drawable.car_ic_notification_2, 0,
             true, false);
 
         mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
@@ -248,9 +228,13 @@
         SeekbarListItem listItem =
             new SeekbarListItem(mContext, volumeMax, currentVolume,
                 new VolumeSeekBarChangeListener(volumeRow), null);
-        listItem.setPrimaryActionIcon(volumeRow.primaryActionIcon);
+        Drawable primaryIcon = mContext.getResources().getDrawable(volumeRow.primaryActionIcon);
+        listItem.setPrimaryActionIcon(primaryIcon);
         if (volumeRow.supplementalIcon != 0) {
-            listItem.setSupplementalIcon(volumeRow.supplementalIcon, true, supplementalIconOnClickListener);
+            Drawable supplementalIcon = mContext.getResources()
+                .getDrawable(volumeRow.supplementalIcon);
+            listItem.setSupplementalIcon(supplementalIcon, true,
+                supplementalIconOnClickListener);
         } else {
             listItem.setSupplementalEmptyIcon(true);
         }
@@ -309,14 +293,14 @@
         mHandler.removeMessages(H.DISMISS);
         mHandler.removeMessages(H.SHOW);
         if (!mShowing) return;
-        mDialogView.animate().cancel();
+        mListView.animate().cancel();
         mShowing = false;
 
-        mDialogView.setTranslationY(0);
-        mDialogView.setAlpha(1);
-        mDialogView.animate()
+        mListView.setTranslationY(0);
+        mListView.setAlpha(1);
+        mListView.animate()
             .alpha(0)
-            .translationY(-mDialogView.getHeight())
+            .translationY(-mListView.getHeight())
             .setDuration(250)
             .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
             .withEndAction(() -> mHandler.postDelayed(() -> {
@@ -487,7 +471,7 @@
 
         @Override
         public void onLayoutDirectionChanged(int layoutDirection) {
-            mDialogView.setLayoutDirection(layoutDirection);
+            mListView.setLayoutDirection(layoutDirection);
         }
 
         @Override
@@ -642,6 +626,45 @@
         }
     }
 
+    private final class ExpandIconListener implements View.OnClickListener {
+        @Override
+        public void onClick(final View v) {
+            mExpanded = !mExpanded;
+            Animator inAnimator;
+            if (mExpanded) {
+                for (VolumeRow row : mRows) {
+                    // Adding the items which are not coming from default stream.
+                    if (!row.defaultStream) {
+                        addSeekbarListItem(row, null);
+                    }
+                }
+                inAnimator = AnimatorInflater.loadAnimator(
+                    mContext, R.anim.car_arrow_fade_in_rotate_up);
+            } else {
+                // Only keeping the default stream if it is not expended.
+                Iterator itr = mVolumeLineItems.iterator();
+                while (itr.hasNext()) {
+                    SeekbarListItem item = (SeekbarListItem) itr.next();
+                    VolumeRow row = findRow(item);
+                    if (!row.defaultStream) {
+                        itr.remove();
+                    }
+                }
+                inAnimator = AnimatorInflater.loadAnimator(
+                    mContext, R.anim.car_arrow_fade_in_rotate_down);
+            }
+
+            Animator outAnimator = AnimatorInflater.loadAnimator(
+                mContext, R.anim.car_arrow_fade_out);
+            inAnimator.setStartDelay(100);
+            AnimatorSet animators = new AnimatorSet();
+            animators.playTogether(outAnimator, inAnimator);
+            animators.setTarget(v);
+            animators.start();
+            mPagedListAdapter.notifyDataSetChanged();
+        }
+    }
+
     private static class VolumeRow {
         private int stream;
         private StreamState ss;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 5c7ce59..b5071de 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -53,7 +53,6 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.text.InputFilter;
@@ -73,8 +72,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
 import android.widget.ImageButton;
-import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
@@ -122,7 +121,7 @@
     private ImageButton mRingerIcon;
     private View mSettingsView;
     private ImageButton mSettingsIcon;
-    private ImageView mZenIcon;
+    private FrameLayout mZenIcon;
     private final List<VolumeRow> mRows = new ArrayList<>();
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -132,7 +131,6 @@
     private final Accessibility mAccessibility = new Accessibility();
     private final ColorStateList mActiveTint;
     private final ColorStateList mInactiveTint;
-    private final Vibrator mVibrator;
 
     private boolean mShowing;
     private boolean mShowA11yStream;
@@ -153,7 +151,6 @@
         mActiveTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
         mInactiveTint = loadColorStateList(R.color.volume_slider_inactive);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
     }
 
     public void init(int windowType, Callback callback) {
@@ -673,7 +670,28 @@
      * @param enable whether to enable volume row views and hide dnd icon
      */
     private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
-        row.dndIcon.setVisibility(enable ? GONE : VISIBLE);
+        boolean showDndIcon = !enable;
+        row.dndIcon.setVisibility(showDndIcon ? VISIBLE : GONE);
+
+        if (showDndIcon && getNumVisibleRows() == 1) {
+            row.dndIcon.setLayoutParams(new FrameLayout.LayoutParams(
+                    mContext.getResources().getDimensionPixelSize(
+                            R.dimen.volume_dialog_panel_width),
+                    FrameLayout.LayoutParams.WRAP_CONTENT));
+        } else if (row.view.getVisibility() == VISIBLE) {
+            row.dndIcon.setLayoutParams(new FrameLayout.LayoutParams(
+                    FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
+        }
+    }
+
+    private int getNumVisibleRows() {
+        int count = 0;
+        for (int i = 0; i < mRows.size(); i++) {
+            if (mRows.get(i).view.getVisibility() == VISIBLE) {
+                count++;
+            }
+        }
+        return count;
     }
 
     /**
@@ -724,8 +742,11 @@
             updateVolumeRowH(row);
         }
         updateRingerH();
-        mWindow.setTitle(mContext.getString(R.string.volume_dialog_title,
-                getStreamLabelH(getActiveRow().ss)));
+        mWindow.setTitle(composeWindowTitle());
+    }
+
+    CharSequence composeWindowTitle() {
+        return mContext.getString(R.string.volume_dialog_title, getStreamLabelH(getActiveRow().ss));
     }
 
     private void updateVolumeRowH(VolumeRow row) {
@@ -1196,6 +1217,13 @@
         }
 
         @Override
+        public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+            // Activities populate their title here. Follow that example.
+            event.getText().add(composeWindowTitle());
+            return true;
+        }
+
+        @Override
         public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
                 AccessibilityEvent event) {
             rescheduleTimeoutH();
@@ -1241,6 +1269,6 @@
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
-        private ImageView dndIcon;
+        private FrameLayout dndIcon;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index fe23541..6792bc0 100644
--- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -14,7 +14,10 @@
 
 package com.android;
 
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
@@ -31,11 +34,11 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
 
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -72,9 +75,9 @@
 
     @Test
     public void testAllClassInheritance() throws Throwable {
-        boolean anyClassWrong = false;
+        ArrayList<String> fails = new ArrayList<>();
         for (String className : getClassNamesFromClassPath()) {
-            Class<?> cls = Class.forName(className, false, this.getClass().getClassLoader());		
+            Class<?> cls = Class.forName(className, false, this.getClass().getClassLoader());
             if (!isTestClass(cls)) continue;
 
             boolean hasParent = false;
@@ -86,17 +89,15 @@
             }
             boolean hasSize = hasSize(cls);
             if (!hasSize) {
-                anyClassWrong = true;
-                Log.e(TAG, cls.getName() + " does not have size annotation, such as @SmallTest");
+                fails.add(cls.getName() + " does not have size annotation, such as @SmallTest");
             }
             if (!hasParent) {
-                anyClassWrong = true;
-                Log.e(TAG, cls.getName() + " does not extend any of " + getClsStr());
+                fails.add(cls.getName() + " does not extend any of " + getClsStr());
             }
         }
 
-        assertFalse("All sysui test classes must have size and extend one of " + getClsStr(),
-                anyClassWrong);
+        assertThat("All sysui test classes must have size and extend one of " + getClsStr(),
+                fails, is(empty()));
     }
 
     private boolean hasSize(Class<?> cls) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 7686948..56bce5d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -16,39 +16,78 @@
 package com.android.keyguard;
 
 import android.graphics.Color;
+import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
 
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Collections;
+import java.util.HashSet;
+
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+
 @SmallTest
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardSliceViewTest extends SysuiTestCase {
     private KeyguardSliceView mKeyguardSliceView;
+    private Uri mSliceUri;
 
     @Before
     public void setUp() throws Exception {
         mKeyguardSliceView = (KeyguardSliceView) LayoutInflater.from(getContext())
                 .inflate(R.layout.keyguard_status_area, null);
+        mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
+        SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST)));
+    }
+
+    @Test
+    public void showSlice_notifiesListener() {
+        ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+        boolean[] notified = {false};
+        mKeyguardSliceView.setContentChangeListener((hasHeader)-> {
+            notified[0] = true;
+        });
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertTrue("Listener should be notified about slice changes.", notified[0]);
+    }
+
+    @Test
+    public void hasHeader_readsSliceData() {
+        ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
+
+        builder.setHeader((ListBuilder.HeaderBuilder headerBuilder) -> {
+            headerBuilder.setTitle("header title!");
+        });
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
     }
 
     @Test
     public void getTextColor_whiteTextWhenAOD() {
         // Set text color to red since the default is white and test would always pass
         mKeyguardSliceView.setTextColor(Color.RED);
-        mKeyguardSliceView.setDark(0);
+        mKeyguardSliceView.setDarkAmount(0);
         Assert.assertEquals("Should be using regular text color", Color.RED,
                 mKeyguardSliceView.getTextColor());
-        mKeyguardSliceView.setDark(1);
+        mKeyguardSliceView.setDarkAmount(1);
         Assert.assertEquals("Should be using AOD text color", Color.WHITE,
                 mKeyguardSliceView.getTextColor());
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 7475cd7..a35ca46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -28,6 +28,8 @@
 import android.testing.LeakCheck;
 import android.util.Log;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -70,6 +72,7 @@
                     "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
         });
         InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
+        KeyguardUpdateMonitor.disableHandlerCheckForTesting(inst);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
index 07ac11b..e529e4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
@@ -132,7 +132,7 @@
      */
 
     @Test
-    public void testSlot_OrderIsPreserved() {
+    public void testSlot_ViewOrder() {
         Slot testSlot = new Slot("test_name", null);
 
         // no tag bc it defaults to 0
@@ -144,17 +144,18 @@
         int sb3Tag = 2;
         when(sbHolder3.getTag()).thenReturn(sb3Tag);
 
-        ArrayList<StatusBarIconHolder> expected = new ArrayList<>();
-        expected.add(sbHolder1);
-        expected.add(sbHolder2);
-        expected.add(sbHolder3);
-
-
         // Add 3 icons in the same slot, and verify that the list we get is equal to what we gave
-        for (StatusBarIconHolder holder : expected) {
-            testSlot.addHolder(holder);
-        }
-        assertTrue(listsEqual(expected, testSlot.getHolderList()));
+        testSlot.addHolder(sbHolder1);
+        testSlot.addHolder(sbHolder2);
+        testSlot.addHolder(sbHolder3);
+
+        // View order is reverse of the order added
+        ArrayList<StatusBarIconHolder> expected = new ArrayList<>();
+        expected.add(sbHolder3);
+        expected.add(sbHolder2);
+        expected.add(sbHolder1);
+
+        assertTrue(listsEqual(expected, testSlot.getHolderListInViewOrder()));
     }
 
     private boolean listsEqual(List<StatusBarIconHolder> list1, List<StatusBarIconHolder> list2) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 550a35d..532019f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -22,6 +22,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.statusbar.phone.DozeParameters.IntInOutMatcher;
 
 import org.junit.Assert;
@@ -218,6 +219,15 @@
         verify(dozeParameters.getPowerManager()).setDozeAfterScreenOff(eq(false));
     }
 
+    @Test
+    public void test_getWallpaperAodDuration_when_shouldControlScreenOff() {
+        TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
+        dozeParameters.setControlScreenOffAnimation(true);
+        Assert.assertEquals("wallpaper hides faster when controlling screen off",
+                dozeParameters.getWallpaperAodDuration(),
+                DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY);
+    }
+
     private class TestableDozeParameters extends DozeParameters {
         private PowerManager mPowerManager;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d3cb5a6..37e0005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -443,6 +443,30 @@
     }
 
     @Test
+    public void testPeek_disabledStatusBar() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+
+        assertFalse("The panel shouldn't allow peek while disabled",
+                mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testPeek_disabledNotificationShade() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+
+        assertFalse("The panel shouldn't allow peek while notitifcation shade disabled",
+                mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 54153a7..d246350 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -16,6 +16,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -162,4 +163,38 @@
             mainLooper.destroy();
         }
     }
+
+    @Test
+    public void testOnServiceConnected_updatesConnectionState() {
+        when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
+
+        mBluetoothControllerImpl.onServiceConnected();
+
+        assertTrue(mBluetoothControllerImpl.isBluetoothConnecting());
+        assertFalse(mBluetoothControllerImpl.isBluetoothConnected());
+    }
+
+    @Test
+    public void testOnBluetoothStateChange_updatesBluetoothState() {
+        mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
+
+        assertEquals(BluetoothAdapter.STATE_OFF, mBluetoothControllerImpl.getBluetoothState());
+
+        mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+        assertEquals(BluetoothAdapter.STATE_ON, mBluetoothControllerImpl.getBluetoothState());
+    }
+
+    @Test
+    public void testOnBluetoothStateChange_updatesConnectionState() {
+        when(mMockAdapter.getConnectionState()).thenReturn(
+                BluetoothAdapter.STATE_CONNECTING,
+                BluetoothAdapter.STATE_DISCONNECTED);
+
+        mBluetoothControllerImpl.onServiceConnected();
+        mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
+
+        assertFalse(mBluetoothControllerImpl.isBluetoothConnecting());
+        assertFalse(mBluetoothControllerImpl.isBluetoothConnected());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
new file mode 100644
index 0000000..43942f7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.util.wakelock;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner.class)
+public class KeepAwakeAnimationListenerTest extends SysuiTestCase {
+    @Mock WakeLock mWakeLock;
+    KeepAwakeAnimationListener mKeepAwakeAnimationListener;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        KeepAwakeAnimationListener.sWakeLock = mWakeLock;
+        mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext());
+    }
+
+    @Test
+    public void onAnimationStart_holdsWakeLock() {
+        mKeepAwakeAnimationListener.onAnimationStart((Animator) null);
+        verify(mWakeLock).acquire();
+        verify(mWakeLock, never()).release();
+
+        mKeepAwakeAnimationListener.onAnimationEnd((Animator) null);
+        verify(mWakeLock).release();
+    }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 5d8a065..c03f537 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5717,6 +5717,24 @@
     // OS: P
     SETTINGS_ZEN_NOTIFICATIONS = 1400;
 
+    // An event category for slices.
+    // OPEN: Slice became visible.
+    // CLOSE: Slice became invisible.
+    // ACTION: Slice was tapped.
+    SLICE = 1401;
+
+    // The authority part of the slice URI
+    FIELD_SLICE_AUTHORITY = 1402;
+
+    // The path part of the slice URI
+    FIELD_SLICE_PATH = 1403;
+
+    // The authority part of the subslice URI
+    FIELD_SUBSLICE_AUTHORITY = 1404;
+
+    // The path part of the subslice URI
+    FIELD_SUBSLICE_PATH = 1405;
+
     // ---- End P Constants, all P constants go above this line ----
 
     // First Q constant in master goes here:
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 3253f2e..51c0488 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -219,6 +219,8 @@
                     final String activePackageName = getActiveAutofillServicePackageName();
                     if (packageName.equals(activePackageName)) {
                         removeCachedServiceLocked(getChangingUserId());
+                    } else {
+                        handlePackageUpdateLocked(packageName);
                     }
                 }
             }
@@ -250,6 +252,8 @@
                                 return true;
                             }
                             removeCachedServiceLocked(getChangingUserId());
+                        } else {
+                          handlePackageUpdateLocked(pkg);
                         }
                     }
                 }
@@ -274,6 +278,14 @@
                 }
                 return serviceComponent.getPackageName();
             }
+
+            @GuardedBy("mLock")
+            private void handlePackageUpdateLocked(String packageName) {
+                final int size = mServicesCache.size();
+                for (int i = 0; i < size; i++) {
+                    mServicesCache.valueAt(i).handlePackageUpdateLocked(packageName);
+                }
+            }
         };
 
         // package changes
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0bb29a7..e582daa 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -626,9 +626,25 @@
     }
 
     @GuardedBy("mLock")
+    void handlePackageUpdateLocked(String packageName) {
+        final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
+        if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
+            resetExtServiceLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    void resetExtServiceLocked() {
+        if (sVerbose) Slog.v(TAG, "reset autofill service.");
+        mFieldClassificationStrategy.reset();
+    }
+
+    @GuardedBy("mLock")
     void destroyLocked() {
         if (sVerbose) Slog.v(TAG, "destroyLocked()");
 
+        resetExtServiceLocked();
+
         final int numSessions = mSessions.size();
         final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions);
         for (int i = 0; i < numSessions; i++) {
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index da52201..9bec856 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -83,7 +83,7 @@
     }
 
     @Nullable
-    private ServiceInfo getServiceInfo() {
+    ServiceInfo getServiceInfo() {
         final String packageName =
                 mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
         if (packageName == null) {
@@ -119,6 +119,18 @@
         return name;
     }
 
+    void reset() {
+        synchronized (mLock) {
+            if (mServiceConnection != null) {
+                if (sDebug) Slog.d(TAG, "reset(): unbinding service.");
+                mContext.unbindService(mServiceConnection);
+                mServiceConnection = null;
+            } else {
+                if (sDebug) Slog.d(TAG, "reset(): service is not bound. Do nothing.");
+            }
+        }
+    }
+
     /**
      * Run a command, starting the service connection if necessary.
      */
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0038e89..bd59ec0 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2079,7 +2079,8 @@
         }
 
         getUiForShowing().showFillUi(filledId, response, filterText,
-                mService.getServicePackageName(), mComponentName.getPackageName(), this);
+                mService.getServicePackageName(), mComponentName.getPackageName(),
+                mService.getServiceLabel(), mService.getServiceIcon(), this);
 
         synchronized (mLock) {
             if (mUiShownTime == 0) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 21a39e4..ee18dc2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -163,11 +163,14 @@
      * @param filterText text of the view to be filled
      * @param servicePackageName package name of the autofill service filling the activity
      * @param packageName package name of the activity that is filled
+     * @param serviceLabel label of autofill service
+     * @param serviceIcon icon of autofill service
      * @param callback Identifier for the caller
      */
     public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
             @Nullable String filterText, @Nullable String servicePackageName,
-            @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
+            @NonNull String packageName, @NonNull CharSequence serviceLabel,
+            @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback) {
         if (sDebug) {
             final int size = filterText == null ? 0 : filterText.length();
             Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
@@ -185,7 +188,7 @@
             }
             hideAllUiThread(callback);
             mFillUi = new FillUi(mContext, response, focusedId,
-                    filterText, mOverlayControl, new FillUi.Callback() {
+                    filterText, mOverlayControl, serviceLabel, serviceIcon, new FillUi.Callback() {
                 @Override
                 public void onResponsePicked(FillResponse response) {
                     log.setType(MetricsEvent.TYPE_DETAIL);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index d29ca05..1aeb3b9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -26,6 +26,8 @@
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
@@ -53,9 +55,11 @@
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.RemoteViews;
+import android.widget.TextView;
 
 import com.android.internal.R;
 import com.android.server.UiThread;
@@ -72,30 +76,10 @@
 final class FillUi {
     private static final String TAG = "FillUi";
 
+    private static final int THEME_ID = com.android.internal.R.style.Theme_DeviceDefault_Autofill;
+
     private static final TypedValue sTempTypedValue = new TypedValue();
 
-    public static final class AutofillFrameLayout extends FrameLayout {
-
-        OnKeyListener mUnhandledListener;
-
-        public AutofillFrameLayout(Context context, AttributeSet attrs) {
-            super(context, attrs);
-        }
-
-        public AutofillFrameLayout(Context context, AttributeSet attrs, @AttrRes int defStyleAttr) {
-            super(context, attrs, defStyleAttr);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            boolean handled = super.dispatchKeyEvent(event);
-            if (!handled) {
-                handled = mUnhandledListener.onKey(this, event.getKeyCode(), event);
-            }
-            return handled;
-        }
-    }
-
     interface Callback {
         void onResponsePicked(@NonNull FillResponse response);
         void onDatasetPicked(@NonNull Dataset dataset);
@@ -146,51 +130,64 @@
 
     FillUi(@NonNull Context context, @NonNull FillResponse response,
            @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
-           @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
-        mContext = context;
+           @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
+           @NonNull Drawable serviceIcon, @NonNull Callback callback) {
         mCallback = callback;
         mFullScreen = isFullScreen(context);
-
-        final LayoutInflater inflater = LayoutInflater.from(context);
+        mContext = new ContextThemeWrapper(context, THEME_ID);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
 
         final RemoteViews headerPresentation = response.getHeader();
         final RemoteViews footerPresentation = response.getFooter();
         final ViewGroup decor;
-        if (headerPresentation != null || footerPresentation != null) {
-            decor = (ViewGroup) inflater.inflate(
-                    mFullScreen ? R.layout.autofill_dataset_picker_header_footer_fullscreen
-                            : R.layout.autofill_dataset_picker_header_footer, null);
+        if (mFullScreen) {
+            decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
+        } else if (headerPresentation != null || footerPresentation != null) {
+            decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
+                    null);
         } else {
-            decor = (ViewGroup) inflater.inflate(
-                    mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
-                            : R.layout.autofill_dataset_picker, null);
+            decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker, null);
+        }
+        final TextView titleView = decor.findViewById(R.id.autofill_dataset_title);
+        if (titleView != null) {
+            titleView.setText(mContext.getString(R.string.autofill_window_title, serviceLabel));
+        }
+        final ImageView iconView = decor.findViewById(R.id.autofill_dataset_icon);
+        if (iconView != null) {
+            iconView.setImageDrawable(serviceIcon);
         }
 
-        // if autofill ui is not fullscreen, send unhandled keyevent to app window.
-        if (!mFullScreen) {
-            if (decor instanceof AutofillFrameLayout) {
-                ((AutofillFrameLayout) decor).mUnhandledListener =
-                        (View view, int keyCode, KeyEvent event) -> {
-                            switch (keyCode) {
-                                case KeyEvent.KEYCODE_BACK:
-                                case KeyEvent.KEYCODE_ESCAPE:
-                                case KeyEvent.KEYCODE_ENTER:
-                                case KeyEvent.KEYCODE_DPAD_CENTER:
-                                case KeyEvent.KEYCODE_DPAD_LEFT:
-                                case KeyEvent.KEYCODE_DPAD_UP:
-                                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                                case KeyEvent.KEYCODE_DPAD_DOWN:
-                                    return false;
-                                default:
-                                    mCallback.dispatchUnhandledKey(event);
-                                    return true;
-                            }
-                        };
-            } else {
-                Slog.wtf(TAG, "Unable to send unhandled key");
+        // In full screen we only initialize size once assuming screen size never changes
+        if (mFullScreen) {
+            final Point outPoint = mTempPoint;
+            mContext.getDisplay().getSize(outPoint);
+            // full with of screen and half height of screen
+            mContentWidth = LayoutParams.MATCH_PARENT;
+            mContentHeight = outPoint.y / 2;
+            if (sVerbose) {
+                Slog.v(TAG, "initialized fillscreen LayoutParams "
+                        + mContentWidth + "," + mContentHeight);
             }
         }
 
+        // Send unhandled keyevent to app window.
+        decor.addOnUnhandledKeyEventListener((View view, KeyEvent event) -> {
+            switch (event.getKeyCode() ) {
+                case KeyEvent.KEYCODE_BACK:
+                case KeyEvent.KEYCODE_ESCAPE:
+                case KeyEvent.KEYCODE_ENTER:
+                case KeyEvent.KEYCODE_DPAD_CENTER:
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                case KeyEvent.KEYCODE_DPAD_UP:
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    return false;
+                default:
+                    mCallback.dispatchUnhandledKey(event);
+                    return true;
+            }
+        });
+
         if (sVisibleDatasetsMaxCount > 0) {
             mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
             if (sVerbose) {
@@ -218,14 +215,12 @@
             mFooter = null;
             mAdapter = null;
 
-            // insert authentication item under autofill_dataset_container or decor
-            ViewGroup container = decor.findViewById(R.id.autofill_dataset_container);
-            if (container == null) {
-                container = decor;
-            }
+            // insert authentication item under autofill_dataset_picker
+            ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
             final View content;
             try {
-                content = response.getPresentation().apply(context, decor, interceptionHandler);
+                response.getPresentation().setApplyTheme(THEME_ID);
+                content = response.getPresentation().apply(mContext, decor, interceptionHandler);
                 container.addView(content);
             } catch (RuntimeException e) {
                 callback.onCanceled();
@@ -236,20 +231,22 @@
             decor.setFocusable(true);
             decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
 
-            final Point maxSize = mTempPoint;
-            resolveMaxWindowSize(context, maxSize);
-            // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
-            content.getLayoutParams().width = mFullScreen ? maxSize.x
-                    : ViewGroup.LayoutParams.WRAP_CONTENT;
-            content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
-            final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
-                    MeasureSpec.AT_MOST);
-            final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
-                    MeasureSpec.AT_MOST);
+            if (!mFullScreen) {
+                final Point maxSize = mTempPoint;
+                resolveMaxWindowSize(mContext, maxSize);
+                // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+                content.getLayoutParams().width = mFullScreen ? maxSize.x
+                        : ViewGroup.LayoutParams.WRAP_CONTENT;
+                content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+                final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
+                        MeasureSpec.AT_MOST);
+                final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
+                        MeasureSpec.AT_MOST);
 
-            decor.measure(widthMeasureSpec, heightMeasureSpec);
-            mContentWidth = content.getMeasuredWidth();
-            mContentHeight = content.getMeasuredHeight();
+                decor.measure(widthMeasureSpec, heightMeasureSpec);
+                mContentWidth = content.getMeasuredWidth();
+                mContentHeight = content.getMeasuredHeight();
+            }
 
             mWindow = new AnchoredWindow(decor, overlayControl);
             requestShowFillUi();
@@ -263,7 +260,8 @@
             RemoteViews.OnClickHandler clickBlocker = null;
             if (headerPresentation != null) {
                 clickBlocker = newClickBlocker();
-                mHeader = headerPresentation.apply(context, null, clickBlocker);
+                headerPresentation.setApplyTheme(THEME_ID);
+                mHeader = headerPresentation.apply(mContext, null, clickBlocker);
                 final LinearLayout headerContainer =
                         decor.findViewById(R.id.autofill_dataset_header);
                 if (sVerbose) Slog.v(TAG, "adding header");
@@ -274,15 +272,21 @@
             }
 
             if (footerPresentation != null) {
-                if (clickBlocker == null) { // already set for header
-                    clickBlocker = newClickBlocker();
-                }
-                mFooter = footerPresentation.apply(context, null, clickBlocker);
                 final LinearLayout footerContainer =
                         decor.findViewById(R.id.autofill_dataset_footer);
-                if (sVerbose) Slog.v(TAG, "adding footer");
-                footerContainer.addView(mFooter);
-                footerContainer.setVisibility(View.VISIBLE);
+                if (footerContainer != null) {
+                    if (clickBlocker == null) { // already set for header
+                        clickBlocker = newClickBlocker();
+                    }
+                    footerPresentation.setApplyTheme(THEME_ID);
+                    mFooter = footerPresentation.apply(mContext, null, clickBlocker);
+                    // Footer not supported on some platform e.g. TV
+                    if (sVerbose) Slog.v(TAG, "adding footer");
+                    footerContainer.addView(mFooter);
+                    footerContainer.setVisibility(View.VISIBLE);
+                } else {
+                    mFooter = null;
+                }
             } else {
                 mFooter = null;
             }
@@ -301,7 +305,8 @@
                     final View view;
                     try {
                         if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
-                        view = presentation.apply(context, null, interceptionHandler);
+                        presentation.setApplyTheme(THEME_ID);
+                        view = presentation.apply(mContext, null, interceptionHandler);
                     } catch (RuntimeException e) {
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
@@ -352,12 +357,7 @@
     }
 
     void requestShowFillUi() {
-        if (mFullScreen) {
-            mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                    mWindowPresenter);
-        } else {
-            mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
-        }
+        mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
     }
 
     /**
@@ -388,12 +388,6 @@
                 mCallback.requestHideFillUi();
             } else {
                 if (updateContentSize()) {
-                    if (mFullScreen) {
-                        LayoutParams lp = mListView.getLayoutParams();
-                        lp.width = mContentWidth;
-                        lp.height = mContentHeight;
-                        mListView.setLayoutParams(lp);
-                    }
                     requestShowFillUi();
                 }
                 if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
@@ -452,6 +446,10 @@
         if (mAdapter == null) {
             return false;
         }
+        if (mFullScreen) {
+            // always request show fill window with fixed size for fullscreen
+            return true;
+        }
         boolean changed = false;
         if (mAdapter.getCount() <= 0) {
             if (mContentWidth != 0) {
@@ -476,11 +474,6 @@
         final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
                 MeasureSpec.AT_MOST);
         final int itemCount = mAdapter.getCount();
-        if (mFullScreen) {
-            // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
-            changed = true;
-            mContentWidth = maxSize.x;
-        }
 
         if (mHeader != null) {
             mHeader.measure(widthMeasureSpec, heightMeasureSpec);
@@ -491,20 +484,9 @@
         for (int i = 0; i < itemCount; i++) {
             final View view = mAdapter.getItem(i).view;
             view.measure(widthMeasureSpec, heightMeasureSpec);
-            if (mFullScreen) {
-                // for fullscreen, add up all children height until hit max height.
-                final int newContentHeight = mContentHeight + view.getMeasuredHeight();
-                final int clampedNewHeight = Math.min(newContentHeight, maxSize.y);
-                if (clampedNewHeight != mContentHeight) {
-                    mContentHeight = clampedNewHeight;
-                } else if (view.getMeasuredHeight() > 0) {
-                    break;
-                }
-            } else {
-                changed |= updateWidth(view, maxSize);
-                if (i < mVisibleDatasetsMaxCount) {
-                    changed |= updateHeight(view, maxSize);
-                }
+            changed |= updateWidth(view, maxSize);
+            if (i < mVisibleDatasetsMaxCount) {
+                changed |= updateHeight(view, maxSize);
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index f96fa7c2..80903c1 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -42,6 +42,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -70,6 +71,9 @@
 
     private static final String TAG = "AutofillSaveUi";
 
+    private static final int THEME_ID =
+            com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
+
     public interface OnSaveListener {
         void onSave();
         void onCancel(IntentSender listener);
@@ -144,6 +148,7 @@
         mServicePackageName = servicePackageName;
         mPackageName = packageName;
 
+        context = new ContextThemeWrapper(context, THEME_ID);
         final LayoutInflater inflater = LayoutInflater.from(context);
         final View view = inflater.inflate(R.layout.autofill_save, null);
 
@@ -222,7 +227,7 @@
         final View yesButton = view.findViewById(R.id.autofill_save_yes);
         yesButton.setOnClickListener((v) -> mListener.onSave());
 
-        mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel);
+        mDialog = new Dialog(context, THEME_ID);
         mDialog.setContentView(view);
 
         // Dialog can be dismissed when touched outside, but the negative listener should not be
@@ -309,6 +314,7 @@
 
         try {
             // Create the remote view peer.
+            template.setApplyTheme(THEME_ID);
             final View customSubtitleView = template.apply(context, null, handler);
 
             // And apply batch updates (if any).
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index ae14dfa..1cd853f 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -56,7 +56,10 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (args != null) {
             for (final String arg : args) {
-                if ("--reset".equals(arg)) {
+                if ("-a".equals(arg)) {
+                    // We currently dump all information by default
+                    continue;
+                } else if ("--reset".equals(arg)) {
                     reset();
                     pw.println("binder_calls_stats reset.");
                     return;
@@ -78,7 +81,6 @@
                     return;
                 } else {
                     pw.println("Unknown option: " + arg);
-                    return;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index aaa42e5..a4d0dc8 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -2411,19 +2411,26 @@
         // after moving out of the inactive state, so no need to worry about that.
         boolean becomeInactive = false;
         if (mState != STATE_ACTIVE) {
-            scheduleReportActiveLocked(type, Process.myUid());
+            // Motion shouldn't affect light state, if it's already in doze-light or maintenance
+            boolean lightIdle = mLightState == LIGHT_STATE_IDLE
+                    || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK
+                    || mLightState == LIGHT_STATE_IDLE_MAINTENANCE;
+            if (!lightIdle) {
+                // Only switch to active state if we're not in either idle state
+                scheduleReportActiveLocked(type, Process.myUid());
+                addEvent(EVENT_NORMAL, type);
+            }
             mState = STATE_ACTIVE;
             mInactiveTimeout = timeout;
             mCurIdleBudget = 0;
             mMaintenanceStartTime = 0;
             EventLogTags.writeDeviceIdle(mState, type);
-            addEvent(EVENT_NORMAL, type);
             becomeInactive = true;
         }
         if (mLightState == LIGHT_STATE_OVERRIDE) {
             // We went out of light idle mode because we had started deep idle mode...  let's
             // now go back and reset things so we resume light idling if appropriate.
-            mLightState = STATE_ACTIVE;
+            mLightState = LIGHT_STATE_ACTIVE;
             EventLogTags.writeDeviceIdleLight(mLightState, type);
             becomeInactive = true;
         }
@@ -2811,6 +2818,8 @@
         pw.println("    If no DURATION is specified, 10 seconds is used");
         pw.println("    If [-r] option is used, then the package is removed from temp whitelist "
                 + "and any [-d] is ignored");
+        pw.println("  motion");
+        pw.println("    Simulate a motion event to bring the device out of deep doze");
     }
 
     class Shell extends ShellCommand {
@@ -3207,13 +3216,28 @@
                 }
             } else {
                 synchronized (this) {
-                    for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
+                    for (int j = 0; j < mPowerSaveWhitelistApps.size(); j++) {
                         pw.print(mPowerSaveWhitelistApps.keyAt(j));
                         pw.print(",");
                         pw.println(mPowerSaveWhitelistApps.valueAt(j));
                     }
                 }
             }
+        } else if ("motion".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    motionLocked();
+                    pw.print("Light state: ");
+                    pw.print(lightStateToString(mLightState));
+                    pw.print(", deep state: ");
+                    pw.println(stateToString(mState));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
         } else {
             return shell.handleDefaultCommands(cmd);
         }
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 2465ba2..d869734 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -33,10 +33,11 @@
 # It logs the time remaining before the device would've normally gone to sleep without the request.
 2731 power_soft_sleep_requested (savedwaketimems|2)
 # Power save state has changed. See BatterySaverController.java for the details.
-2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
+2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5),(reason|1|5)
 27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
 # Note when the user activity timeout has been overriden by ActivityManagerService
 27391 user_activity_timeout_override (override|2|3)
+27392 battery_saver_setting (threshold|1)
 
 #
 # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 021bfaa..fe30057 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Trace;
 
 /**
@@ -28,6 +29,9 @@
  * to be delayed for a user-noticeable amount of time.
  */
 public final class FgThread extends ServiceThread {
+    private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
+    private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
+
     private static FgThread sInstance;
     private static Handler sHandler;
 
@@ -39,7 +43,10 @@
         if (sInstance == null) {
             sInstance = new FgThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+            final Looper looper = sInstance.getLooper();
+            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+            looper.setSlowLogThresholdMs(
+                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 83fe976..41f413d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -351,7 +351,7 @@
             mCallForwarding[i] =  false;
             mCellLocation[i] = new Bundle();
             mCellInfo.add(i, null);
-            mPhysicalChannelConfigs.add(i, null);
+            mPhysicalChannelConfigs.add(i, new ArrayList<PhysicalChannelConfig>());
         }
 
         // Note that location can be null for non-phone builds like
@@ -1426,6 +1426,31 @@
         }
     }
 
+    public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+        if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
+            return;
+        }
+
+        synchronized (mRecords) {
+            for (Record r : mRecords) {
+                if (VDBG) {
+                    log("notifyOemHookRawEventForSubscriber:  r=" + r + " subId=" + subId);
+                }
+                if ((r.matchPhoneStateListenerEvent(
+                        PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
+                        ((r.subId == subId) ||
+                        (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
+                    try {
+                        r.callback.onOemHookRawEvent(rawData);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -1693,6 +1718,11 @@
                     android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
         }
 
+        if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+        }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index f813074..b2fa684 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -28,6 +28,7 @@
  */
 public final class UiThread extends ServiceThread {
     private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
+    private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
     private static UiThread sInstance;
     private static Handler sHandler;
 
@@ -48,7 +49,8 @@
             sInstance.start();
             final Looper looper = sInstance.getLooper();
             looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
-            looper.setSlowDispatchThresholdMs(SLOW_DISPATCH_THRESHOLD_MS);
+            looper.setSlowLogThresholdMs(
+                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 566e128..59093c1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -92,6 +92,7 @@
 
     public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
         "android.hardware.audio@2.0::IDevicesFactory",
+        "android.hardware.audio@4.0::IDevicesFactory",
         "android.hardware.bluetooth@1.0::IBluetoothHci",
         "android.hardware.camera.provider@2.4::ICameraProvider",
         "android.hardware.graphics.composer@2.1::IComposer",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 228171f..f413639 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -422,13 +422,9 @@
         }
 
         // If we're starting indirectly (e.g. from PendingIntent), figure out whether
-        // we're launching into an app in a background state.
-        final int uidState = mAm.getUidStateLocked(r.appInfo.uid);
-        if (DEBUG_SERVICE) {
-            Slog.v(TAG_SERVICE, "Uid state " + uidState + " indirect starting " + r.shortName);
-        }
-        final boolean bgLaunch = (uidState >
-                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+        // we're launching into an app in a background state.  This keys off of the same
+        // idleness state tracking as e.g. O+ background service start policy.
+        final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);
 
         // If the app has strict background restrictions, we treat any bg service
         // start analogously to the legacy-app forced-restrictions case, regardless
@@ -1197,10 +1193,13 @@
 
                 if (!ignoreForeground &&
                         appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
-                    ignoreForeground = true;
                     Slog.w(TAG,
                             "Service.startForeground() not allowed due to bg restriction: service "
                             + r.shortName);
+                    // Back off of any foreground expectations around this service, since we've
+                    // just turned down its fg request.
+                    updateServiceForegroundLocked(r.app, false);
+                    ignoreForeground = true;
                 }
 
                 // Apps under strict background restrictions simply don't get to have foreground
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index fac3f92..0f42103 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -420,7 +420,9 @@
                 if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                     continue;
                 }
-                otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN, false /* animate */,
+                        false /* showRecents */, false /* enteringSplitScreenMode */,
+                        true /* deferEnsuringVisibility */);
             }
         } finally {
             final ActivityStack topFullscreenStack =
@@ -450,7 +452,7 @@
                 }
                 otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
                         false /* animate */, false /* showRecents */,
-                        true /* enteringSplitScreenMode */);
+                        true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
             }
         } finally {
             mSupervisor.mWindowManager.continueSurfaceLayout();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8a4a8a..d54fee8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11145,7 +11145,7 @@
                     stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
                 }
                 stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
-                        false /* enteringSplitScreenMode */);
+                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
                 return windowingMode != task.getWindowingMode();
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -26500,8 +26500,7 @@
         @Override
         public boolean isUidActive(int uid) {
             synchronized (ActivityManagerService.this) {
-                final UidRecord uidRec = mActiveUids.get(uid);
-                return (uidRec != null) && !uidRec.idle;
+                return isUidActiveLocked(uid);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 16c5969..e73f42f 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1626,6 +1626,10 @@
         if (parent != null) {
             parent.onActivityStateChanged(this, state, reason);
         }
+
+        if (state == STOPPING) {
+            mWindowContainerController.notifyAppStopping();
+        }
     }
 
     ActivityState getState() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9fb3e11..e86850e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -515,11 +515,11 @@
     @Override
     public void setWindowingMode(int windowingMode) {
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
-                false /* enteringSplitScreenMode */);
+                false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
     }
 
     void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
-            boolean enteringSplitScreenMode) {
+            boolean enteringSplitScreenMode, boolean deferEnsuringVisibility) {
         final boolean creating = mWindowContainerController == null;
         final int currentMode = getWindowingMode();
         final ActivityDisplay display = getDisplay();
@@ -555,7 +555,9 @@
                 // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
                 // warning toast about it.
                 mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
-                display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN,
+                        false /* animate */, false /* showRecents */,
+                        false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */);
             }
         }
 
@@ -646,9 +648,7 @@
             wm.continueSurfaceLayout();
         }
 
-        // Don't ensure visible activities if the windowing mode change was a side effect of us
-        // entering split-screen mode.
-        if (!enteringSplitScreenMode) {
+        if (!deferEnsuringVisibility) {
             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
             mStackSupervisor.resumeFocusedStackTopActivityLocked();
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e5565dc..548290e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2409,6 +2409,16 @@
                 if (stack.isCompatible(windowingMode, activityType)) {
                     return stack;
                 }
+                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+                        && display.getSplitScreenPrimaryStack() == stack
+                        && candidateTask == stack.topTask()) {
+                    // This is a special case when we try to launch an activity that is currently on
+                    // top of split-screen primary stack, but is targeting split-screen secondary.
+                    // In this case we don't want to move it to another stack.
+                    // TODO(b/78788972): Remove after differentiating between preferred and required
+                    // launch options.
+                    return stack;
+                }
             }
         }
 
@@ -2890,10 +2900,8 @@
         try {
             ActivityRecord r = stack.topRunningActivityLocked();
             Rect insetBounds = null;
-            if (tempPinnedTaskBounds != null) {
-                // We always use 0,0 as the position for the inset rect because
-                // if we are getting insets at all in the pinned stack it must mean
-                // we are headed for fullscreen.
+            if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
+                // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
                 insetBounds = tempRect;
                 insetBounds.top = 0;
                 insetBounds.left = 0;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 87194bc..49fa902 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -103,6 +103,9 @@
     @GuardedBy("this")
     private Future<?> mWakelockChangesUpdate;
 
+    @GuardedBy("this")
+    private Future<?> mBatteryLevelSync;
+
     private final Object mWorkerLock = new Object();
 
     @GuardedBy("mWorkerLock")
@@ -197,35 +200,75 @@
 
     @Override
     public Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis) {
-        if (mExecutorService.isShutdown()) {
-            return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
+        synchronized (BatteryExternalStatsWorker.this) {
+            mWakelockChangesUpdate = scheduleDelayedSyncLocked(mWakelockChangesUpdate,
+                    () -> {
+                        scheduleSync("wakelock-change", UPDATE_CPU);
+                        scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg());
+                    },
+                    delayMillis);
+            return mWakelockChangesUpdate;
         }
-
-        if (mWakelockChangesUpdate != null) {
-            // If there's already a scheduled task, leave it as is if we're trying to re-schedule
-            // it again with a delay, otherwise cancel and re-schedule it.
-            if (delayMillis == 0) {
-                mWakelockChangesUpdate.cancel(false);
-            } else {
-                return mWakelockChangesUpdate;
-            }
-        }
-
-        mWakelockChangesUpdate = mExecutorService.schedule(() -> {
-            scheduleSync("wakelock-change", UPDATE_CPU);
-            scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg());
-            mWakelockChangesUpdate = null;
-        }, delayMillis, TimeUnit.MILLISECONDS);
-        return mWakelockChangesUpdate;
     }
 
     @Override
     public void cancelCpuSyncDueToWakelockChange() {
-        if (mWakelockChangesUpdate != null) {
-            mWakelockChangesUpdate.cancel(false);
+        synchronized (BatteryExternalStatsWorker.this) {
+            if (mWakelockChangesUpdate != null) {
+                mWakelockChangesUpdate.cancel(false);
+                mWakelockChangesUpdate = null;
+            }
         }
     }
 
+    @Override
+    public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
+        synchronized (BatteryExternalStatsWorker.this) {
+            mBatteryLevelSync = scheduleDelayedSyncLocked(mBatteryLevelSync,
+                    () -> scheduleSync("battery-level", UPDATE_ALL),
+                    delayMillis);
+            return mBatteryLevelSync;
+        }
+    }
+
+    @GuardedBy("this")
+    private void cancelSyncDueToBatteryLevelChangeLocked() {
+        if (mBatteryLevelSync != null) {
+            mBatteryLevelSync.cancel(false);
+            mBatteryLevelSync = null;
+        }
+    }
+
+    /**
+     * Schedule a sync {@param syncRunnable} with a delay. If there's already a scheduled sync, a
+     * new sync won't be scheduled unless it is being scheduled to run immediately (delayMillis=0).
+     *
+     * @param lastScheduledSync the task which was earlier scheduled to run
+     * @param syncRunnable the task that needs to be scheduled to run
+     * @param delayMillis time after which {@param syncRunnable} needs to be scheduled
+     * @return scheduled {@link Future} which can be used to check if task is completed or to
+     *         cancel it if needed
+     */
+    @GuardedBy("this")
+    private Future<?> scheduleDelayedSyncLocked(Future<?> lastScheduledSync, Runnable syncRunnable,
+            long delayMillis) {
+        if (mExecutorService.isShutdown()) {
+            return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
+        }
+
+        if (lastScheduledSync != null) {
+            // If there's already a scheduled task, leave it as is if we're trying to
+            // re-schedule it again with a delay, otherwise cancel and re-schedule it.
+            if (delayMillis == 0) {
+                lastScheduledSync.cancel(false);
+            } else {
+                return lastScheduledSync;
+            }
+        }
+
+        return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS);
+    }
+
     public synchronized Future<?> scheduleWrite() {
         if (mExecutorService.isShutdown()) {
             return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
@@ -294,6 +337,12 @@
                 mUidsToRemove.clear();
                 mCurrentFuture = null;
                 mUseLatestStates = true;
+                if ((updateFlags & UPDATE_ALL) != 0) {
+                    cancelSyncDueToBatteryLevelChangeLocked();
+                }
+                if ((updateFlags & UPDATE_CPU) != 0) {
+                    cancelCpuSyncDueToWakelockChange();
+                }
             }
 
             try {
diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
index 676f0c7..1149e87 100644
--- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
@@ -23,6 +23,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
+import android.os.UserManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
@@ -59,11 +60,14 @@
         View view = LayoutInflater.from(getContext()).inflate(R.layout.car_user_switching_dialog,
             null);
 
-        FileDescriptor fileDescriptor = UserManagerService.getInstance()
-                .getUserIcon(mNewUser.id).getFileDescriptor();
-        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
-        ((ImageView) view.findViewById(R.id.user_loading_avatar))
-            .setImageBitmap(bitmap);
+        UserManager userManager =
+                (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        Bitmap bitmap = userManager.getUserIcon(mNewUser.id);
+        if (bitmap != null) {
+            ((ImageView) view.findViewById(R.id.user_loading_avatar))
+                    .setImageBitmap(bitmap);
+        }
+
         ((TextView) view.findViewById(R.id.user_loading))
             .setText(res.getString(R.string.car_loading_profile));
         setView(view);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 483fec6..e0aa2a2 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -283,6 +283,14 @@
                 final int callingUid = Binder.getCallingUid();
                 final int callingPid = Binder.getCallingPid();
 
+                // Extract options before clearing calling identity
+                SafeActivityOptions mergedOptions = key.options;
+                if (mergedOptions == null) {
+                    mergedOptions = SafeActivityOptions.fromBundle(options);
+                } else {
+                    mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+                }
+
                 final long origId = Binder.clearCallingIdentity();
 
                 if (whitelistDuration != null) {
@@ -319,13 +327,6 @@
                 switch (key.type) {
                     case ActivityManager.INTENT_SENDER_ACTIVITY:
                         try {
-                            SafeActivityOptions mergedOptions = key.options;
-                            if (mergedOptions == null) {
-                                mergedOptions = SafeActivityOptions.fromBundle(options);
-                            } else {
-                                mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
-                            }
-
                             // Note when someone has a pending intent, even from different
                             // users, then there's no need to ensure the calling user matches
                             // the target user, so validateIncomingUser is always false below.
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index c83bacb..06a3078 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -22,8 +22,10 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -1255,7 +1257,8 @@
         for (int i = 0; i < recentsCount; i++) {
             final TaskRecord tr = mTasks.get(i);
             if (task != tr) {
-                if (!task.hasCompatibleActivityType(tr) || task.userId != tr.userId) {
+                if (!hasCompatibleActivityTypeAndWindowingMode(task, tr)
+                        || task.userId != tr.userId) {
                     continue;
                 }
                 final Intent trIntent = tr.intent;
@@ -1547,4 +1550,29 @@
 
         return rti;
     }
+
+    /**
+     * @return Whether the activity types and windowing modes of the two tasks are considered
+     *         compatible. This is necessary because we currently don't persist the activity type
+     *         or the windowing mode with the task, so they can be undefined when restored.
+     */
+    private boolean hasCompatibleActivityTypeAndWindowingMode(TaskRecord t1, TaskRecord t2) {
+        final int activityType = t1.getActivityType();
+        final int windowingMode = t1.getWindowingMode();
+        final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED;
+        final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED;
+        final int otherActivityType = t2.getActivityType();
+        final int otherWindowingMode = t2.getWindowingMode();
+        final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED;
+        final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
+
+        // An activity type and windowing mode is compatible if they are the exact same type/mode,
+        // or if one of the type/modes is undefined
+        final boolean isCompatibleType = activityType == otherActivityType
+                || isUndefinedType || isOtherUndefinedType;
+        final boolean isCompatibleMode = windowingMode == otherWindowingMode
+                || isUndefinedMode || isOtherUndefinedMode;
+
+        return isCompatibleType && isCompatibleMode;
+    }
 }
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index b14f5b6..b5047ae 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -101,9 +101,7 @@
                         : ACTIVITY_TYPE_HOME;
         final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
                 mTargetActivityType);
-        ActivityRecord targetActivity = targetStack != null
-                ? targetStack.getTopActivity()
-                : null;
+        ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
         final boolean hasExistingActivity = targetActivity != null;
         if (hasExistingActivity) {
             final ActivityDisplay display = targetActivity.getDisplay();
@@ -149,6 +147,14 @@
                 display.moveStackBehindBottomMostVisibleStack(targetStack);
                 if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
                             + display.getStackAbove(targetStack));
+
+                // If there are multiple tasks in the target stack (ie. the home stack, with 3p
+                // and default launchers coexisting), then move the task to the top as a part of
+                // moving the stack to the front
+                if (targetStack.topTask() != targetActivity.getTask()) {
+                    targetStack.addTask(targetActivity.getTask(), true /* toTop */,
+                            "startRecentsActivity");
+                }
             } else {
                 // No recents activity
                 ActivityOptions options = ActivityOptions.makeBasic();
@@ -332,4 +338,22 @@
         }
         return null;
     }
+
+    /**
+     * @return the top activity in the {@param targetStack} matching the {@param component}, or just
+     * the top activity of the top task if no task matches the component.
+     */
+    private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) {
+        if (targetStack == null) {
+            return null;
+        }
+
+        for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
+            final TaskRecord task = (TaskRecord) targetStack.getChildAt(i);
+            if (task.getBaseIntent().getComponent().equals(component)) {
+                return task.getTopActivity();
+            }
+        }
+        return targetStack.getTopActivity();
+    }
 }
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index 2de75273..0fb69e7 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -32,6 +32,7 @@
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.UserHandle;
 import android.util.Slog;
 import android.view.RemoteAnimationAdapter;
@@ -78,6 +79,9 @@
         mOriginalCallingPid = Binder.getCallingPid();
         mOriginalCallingUid = Binder.getCallingUid();
         mOriginalOptions = options;
+        if (mOriginalCallingPid == Process.myPid()) {
+            Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
+        }
     }
 
     /**
@@ -89,6 +93,9 @@
         mRealCallingPid = Binder.getCallingPid();
         mRealCallingUid = Binder.getCallingUid();
         mCallerOptions = options;
+        if (mRealCallingPid == Process.myPid()) {
+            Slog.wtf(TAG, "setCallerOptions called after clearing calling id");
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 1cd1dd6..9a5b1a6 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1820,7 +1820,7 @@
             final StackWindowController stackController = mStack.getWindowContainerController();
             stackController.adjustConfigurationForBounds(bounds, insetBounds,
                     mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
-                    config, parentConfig);
+                    config, parentConfig, getWindowingMode());
         } else {
             throw new IllegalArgumentException("Expected stack when calculating override config");
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index db3efab..324df41 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5671,8 +5671,6 @@
         // enable A2DP before notifying A2DP connection to avoid unnecessary processing in
         // audio policy manager
         VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
-        sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
-                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
         setBluetoothA2dpOnInt(true, eventSource);
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
@@ -5940,12 +5938,6 @@
         // address is not used for now, but may be used when multiple a2dp devices are supported
         synchronized (mA2dpAvrcpLock) {
             mAvrcpAbsVolSupported = support;
-            sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
-                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
-                    mStreamStates[AudioSystem.STREAM_MUSIC], 0);
-            sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
-                    AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
-                    mStreamStates[AudioSystem.STREAM_RING], 0);
         }
     }
 
@@ -7015,6 +7007,7 @@
     final int LOG_NB_EVENTS_WIRED_DEV_CONNECTION = 30;
     final int LOG_NB_EVENTS_FORCE_USE = 20;
     final int LOG_NB_EVENTS_VOLUME = 40;
+    final int LOG_NB_EVENTS_DYN_POLICY = 10;
 
     final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
             "phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
@@ -7031,6 +7024,9 @@
     final private AudioEventLogger mVolumeLogger = new AudioEventLogger(LOG_NB_EVENTS_VOLUME,
             "volume changes (logged when command received by AudioService)");
 
+    final private AudioEventLogger mDynPolicyLogger = new AudioEventLogger(LOG_NB_EVENTS_DYN_POLICY,
+            "dynamic policy events (logged when command received by AudioService)");
+
     private static final String[] RINGER_MODE_NAMES = new String[] {
             "SILENT",
             "VIBRATE",
@@ -7098,6 +7094,7 @@
         pw.print("  mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
 
         dumpAudioPolicies(pw);
+        mDynPolicyLogger.dump(pw);
 
         mPlaybackMonitor.dump(pw);
 
@@ -7422,8 +7419,6 @@
             boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
         AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
 
-        if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
-                + " with config:" + policyConfig);
         String regId = null;
         // error handling
         boolean hasPermissionForPolicy =
@@ -7435,6 +7430,8 @@
             return null;
         }
 
+        mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for "
+                + pcb.asBinder() + " with config:" + policyConfig)).printLog(TAG));
         synchronized (mAudioPolicies) {
             try {
                 if (mAudioPolicies.containsKey(pcb.asBinder())) {
@@ -7457,7 +7454,8 @@
     }
 
     public void unregisterAudioPolicyAsync(IAudioPolicyCallback pcb) {
-        if (DEBUG_AP) Log.d(TAG, "unregisterAudioPolicyAsync for " + pcb.asBinder());
+        mDynPolicyLogger.log((new AudioEventLogger.StringEvent("unregisterAudioPolicyAsync for "
+                + pcb.asBinder()).printLog(TAG)));
         synchronized (mAudioPolicies) {
             AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder());
             if (app == null) {
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 34309b6..2feea41 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -103,6 +103,9 @@
                 conf.dump(pw);
             }
         }
+        pw.println("\n");
+        // log
+        sEventLogger.dump(pw);
     }
 
     private ArrayList<AudioRecordingConfiguration> anonymizeForPublicConsumption(
@@ -190,6 +193,9 @@
             case AudioManager.RECORD_CONFIG_EVENT_STOP:
                 // return failure if an unknown recording session stopped
                 configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
+                if (configChanged) {
+                    sEventLogger.log(new RecordingEvent(event, uid, session, source, null));
+                }
                 break;
             case AudioManager.RECORD_CONFIG_EVENT_START:
                 final AudioFormat clientFormat = new AudioFormat.Builder()
@@ -231,6 +237,9 @@
                     mRecordConfigs.put(sessionKey, updatedConfig);
                     configChanged = true;
                 }
+                if (configChanged) {
+                    sEventLogger.log(new RecordingEvent(event, uid, session, source, packageName));
+                }
                 break;
             default:
                 Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
@@ -281,4 +290,36 @@
             mDispatcherCb.asBinder().unlinkToDeath(this, 0);
         }
     }
+
+    /**
+     * Inner class for recording event logging
+     */
+    private static final class RecordingEvent extends AudioEventLogger.Event {
+        private final int mRecEvent;
+        private final int mClientUid;
+        private final int mSession;
+        private final int mSource;
+        private final String mPackName;
+
+        RecordingEvent(int event, int uid, int session, int source, String packName) {
+            mRecEvent = event;
+            mClientUid = uid;
+            mSession = session;
+            mSource = source;
+            mPackName = packName;
+        }
+
+        @Override
+        public String eventToString() {
+            return new StringBuilder("rec ").append(
+                        mRecEvent == AudioManager.RECORD_CONFIG_EVENT_START ? "start" : "stop ")
+                    .append(" uid:").append(mClientUid)
+                    .append(" session:").append(mSession)
+                    .append(" src:").append(MediaRecorder.toLogFriendlyAudioSource(mSource))
+                    .append(mPackName == null ? "" : " pack:" + mPackName).toString();
+        }
+    }
+
+    private static final AudioEventLogger sEventLogger = new AudioEventLogger(50,
+            "recording activity as reported through AudioSystem.AudioRecordingCallback");
 }
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index e840a29..03d8f39 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1279,7 +1279,9 @@
                 case Process.SYSTEM_UID:
                     break; // Okay
                 default:
-                    throw new SecurityException("Invalid extras specified.");
+                    final String msg = "Invalid extras specified.";
+                    Log.w(TAG, msg + " requestsync -f/-F needs to run on 'adb shell'");
+                    throw new SecurityException(msg);
             }
         }
     }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index a55870f..5fa4245 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -68,6 +68,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -454,6 +455,7 @@
         }
     };
 
+    private final HandlerThread mThread;
     private final SyncHandler mSyncHandler;
     private final SyncManagerConstants mConstants;
 
@@ -604,7 +606,9 @@
 
         mSyncAdapters = new SyncAdaptersCache(mContext);
 
-        mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
+        mThread = new HandlerThread("SyncManager", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+        mThread.start();
+        mSyncHandler = new SyncHandler(mThread.getLooper());
 
         mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
             @Override
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index f403953..0425844 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -316,7 +316,7 @@
         return true;
     }
 
-    private void resetShortTermModel() {
+    public void resetShortTermModel() {
         mBrightnessMapper.clearUserDataPoints();
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 37035c3..030c915 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -293,7 +293,7 @@
             mColorMatrixAnimator.cancel();
         }
 
-        setCoefficientMatrix(getContext(), mode == ColorDisplayController.COLOR_MODE_SATURATED);
+        setCoefficientMatrix(getContext(), DisplayTransformManager.isColorModeNative(mode));
         setMatrix(mController.getColorTemperature(), mMatrixNight);
 
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 46e883c..3b35d02 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -774,14 +774,8 @@
             brightness = mScreenBrightnessForVr;
         }
 
-        boolean setBrightnessToOverride = false;
         if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
             brightness = mPowerRequest.screenBrightnessOverride;
-            // If there's a screen brightness override, we want to reset the brightness to it
-            // whenever the user changes it, to communicate that these changes aren't taking
-            // effect. However, for a nicer user experience, we don't do it here, but rather after
-            // the temporary brightness has been taken into account.
-            setBrightnessToOverride = true;
         }
 
         final boolean autoBrightnessEnabledInDoze =
@@ -804,12 +798,6 @@
             brightnessIsTemporary = true;
         }
 
-        // Reset the brightness to the screen brightness override to communicate to the user that
-        // her changes aren't taking effect.
-        if (setBrightnessToOverride && !brightnessIsTemporary) {
-            putScreenBrightnessSetting(brightness);
-        }
-
         final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
         if (autoBrightnessAdjustmentChanged) {
             mTemporaryAutoBrightnessAdjustment = Float.NaN;
@@ -1452,6 +1440,9 @@
         if (userSwitch) {
             // Don't treat user switches as user initiated change.
             mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
+            if (mAutomaticBrightnessController != null) {
+                mAutomaticBrightnessController.resetShortTermModel();
+            }
         }
         mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         // We don't bother with a pending variable for VR screen brightness since we just
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index a94f049..57d88e1 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -60,14 +60,24 @@
     private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
 
     private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
-    private static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode";
+    private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
 
+    /**
+     * SurfaceFlinger global saturation factor.
+     */
     private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022;
-    private static final int SURFACE_FLINGER_TRANSACTION_NATIVE_MODE = 1023;
+    /**
+     * SurfaceFlinger display color (managed, unmanaged, etc.).
+     */
+    private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
 
     private static final float COLOR_SATURATION_NATURAL = 1.0f;
     private static final float COLOR_SATURATION_BOOSTED = 1.1f;
 
+    private static final int DISPLAY_COLOR_MANAGED = 0;
+    private static final int DISPLAY_COLOR_UNMANAGED = 1;
+    private static final int DISPLAY_COLOR_ENHANCED = 2;
+
     /**
      * Map of level -> color transformation matrix.
      */
@@ -220,20 +230,37 @@
         }
     }
 
+    /**
+     * Return true when colors are stretched from the working color space to the
+     * native color space.
+     */
     public static boolean isNativeModeEnabled() {
-        return SystemProperties.getBoolean(PERSISTENT_PROPERTY_NATIVE_MODE, false);
+        return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
+                DISPLAY_COLOR_MANAGED) != DISPLAY_COLOR_MANAGED;
+    }
+
+    /**
+     * Return true when the specified colorMode stretches colors from the
+     * working color space to the native color space.
+     */
+    public static boolean isColorModeNative(int colorMode) {
+        return !(colorMode == ColorDisplayController.COLOR_MODE_NATURAL ||
+                 colorMode == ColorDisplayController.COLOR_MODE_BOOSTED);
     }
 
     public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) {
         if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
             applySaturation(COLOR_SATURATION_NATURAL);
-            setNativeMode(false);
+            setDisplayColor(DISPLAY_COLOR_MANAGED);
         } else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
             applySaturation(COLOR_SATURATION_BOOSTED);
-            setNativeMode(false);
+            setDisplayColor(DISPLAY_COLOR_MANAGED);
         } else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
             applySaturation(COLOR_SATURATION_NATURAL);
-            setNativeMode(true);
+            setDisplayColor(DISPLAY_COLOR_UNMANAGED);
+        } else if (colorMode == ColorDisplayController.COLOR_MODE_AUTOMATIC) {
+            applySaturation(COLOR_SATURATION_NATURAL);
+            setDisplayColor(DISPLAY_COLOR_ENHANCED);
         }
         setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix);
 
@@ -265,17 +292,17 @@
     /**
      * Toggles native mode on/off in SurfaceFlinger.
      */
-    private void setNativeMode(boolean enabled) {
-        SystemProperties.set(PERSISTENT_PROPERTY_NATIVE_MODE, enabled ? "1" : "0");
+    private void setDisplayColor(int color) {
+        SystemProperties.set(PERSISTENT_PROPERTY_DISPLAY_COLOR, Integer.toString(color));
         final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
         if (flinger != null) {
             final Parcel data = Parcel.obtain();
             data.writeInterfaceToken("android.ui.ISurfaceComposer");
-            data.writeInt(enabled ? 1 : 0);
+            data.writeInt(color);
             try {
-                flinger.transact(SURFACE_FLINGER_TRANSACTION_NATIVE_MODE, data, null, 0);
+                flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
             } catch (RemoteException ex) {
-                Log.e(TAG, "Failed to set native mode", ex);
+                Log.e(TAG, "Failed to set display color", ex);
             } finally {
                 data.recycle();
             }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ef40a1c..c9f92d2 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -619,6 +619,15 @@
 
     void startRemove(IBinder token, int fingerId, int groupId, int userId,
             IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
+        if (token == null) {
+            Slog.w(TAG, "startRemove: token is null");
+            return;
+        }
+        if (receiver == null) {
+            Slog.w(TAG, "startRemove: receiver is null");
+            return;
+        }
+
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "startRemove: no fingerprint HAL!");
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 03046b6..64750b0 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -827,26 +827,6 @@
 
         mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mHandler) {
             @Override
-            protected boolean isAvailableInPlatform() {
-                return native_is_navigation_message_supported();
-            }
-
-            @Override
-            protected int registerWithService() {
-                boolean result = native_start_navigation_message_collection();
-                if (result) {
-                    return RemoteListenerHelper.RESULT_SUCCESS;
-                } else {
-                    return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
-                }
-            }
-
-            @Override
-            protected void unregisterFromService() {
-                native_stop_navigation_message_collection();
-            }
-
-            @Override
             protected boolean isGpsEnabled() {
                 return isEnabled();
             }
@@ -2760,13 +2740,6 @@
     private native void native_update_network_state(boolean connected, int type,
             boolean roaming, boolean available, String extraInfo, String defaultAPN);
 
-    // Gps Navigation message support.
-    private static native boolean native_is_navigation_message_supported();
-
-    private native boolean native_start_navigation_message_collection();
-
-    private native boolean native_stop_navigation_message_collection();
-
     // GNSS Configuration
     private static native boolean native_set_supl_version(int version);
 
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index df3c49b..1b4fd18 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -22,6 +22,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * An base implementation for GPS navigation messages provider.
  * It abstracts out the responsibility of handling listeners, while still allowing technology
@@ -32,9 +34,53 @@
 public abstract class GnssNavigationMessageProvider
         extends RemoteListenerHelper<IGnssNavigationMessageListener> {
     private static final String TAG = "GnssNavigationMessageProvider";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final GnssNavigationMessageProviderNative mNative;
+    private boolean mCollectionStarted;
 
     protected GnssNavigationMessageProvider(Handler handler) {
+        this(handler, new GnssNavigationMessageProviderNative());
+    }
+
+    @VisibleForTesting
+    GnssNavigationMessageProvider(Handler handler, GnssNavigationMessageProviderNative aNative) {
         super(handler, TAG);
+        mNative = aNative;
+    }
+
+    // TODO(b/37460011): Use this with death recovery logic.
+    void resumeIfStarted() {
+        if (DEBUG) {
+            Log.d(TAG, "resumeIfStarted");
+        }
+        if (mCollectionStarted) {
+            mNative.startNavigationMessageCollection();
+        }
+    }
+
+    @Override
+    protected boolean isAvailableInPlatform() {
+        return mNative.isNavigationMessageSupported();
+    }
+
+    @Override
+    protected int registerWithService() {
+        boolean result = mNative.startNavigationMessageCollection();
+        if (result) {
+            mCollectionStarted = true;
+            return RemoteListenerHelper.RESULT_SUCCESS;
+        } else {
+            return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+        }
+    }
+
+    @Override
+    protected void unregisterFromService() {
+        boolean stopped = mNative.stopNavigationMessageCollection();
+        if (stopped) {
+            mCollectionStarted = false;
+        }
     }
 
     public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
@@ -96,4 +142,25 @@
             listener.onStatusChanged(mStatus);
         }
     }
+
+    @VisibleForTesting
+    static class GnssNavigationMessageProviderNative {
+        public boolean isNavigationMessageSupported() {
+            return native_is_navigation_message_supported();
+        }
+
+        public boolean startNavigationMessageCollection() {
+            return native_start_navigation_message_collection();
+        }
+
+        public boolean stopNavigationMessageCollection() {
+            return native_stop_navigation_message_collection();
+        }
+    }
+
+    private static native boolean native_is_navigation_message_supported();
+
+    private static native boolean native_start_navigation_message_collection();
+
+    private static native boolean native_stop_navigation_message_collection();
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 17f6d5c..32999bb 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -711,7 +712,7 @@
                 StatusBarNotification sbn = r.sbn;
                 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
                         sbn.getId(), Notification.FLAG_AUTO_CANCEL,
-                        Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
+                        FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
                         REASON_CLICK, nv.rank, nv.count, null);
                 nv.recycle();
                 reportUserInteraction(r);
@@ -755,7 +756,7 @@
                 }
             }
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
-                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+                    Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
                     true, userId, REASON_CANCEL, nv.rank, nv.count,null);
             nv.recycle();
         }
@@ -986,7 +987,7 @@
                     cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
                             record.sbn.getPackageName(), record.sbn.getTag(),
                             record.sbn.getId(), 0,
-                            Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
+                            FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
                             REASON_TIMEOUT, null);
                 }
             }
@@ -2085,7 +2086,7 @@
             // Don't allow client applications to cancel foreground service notis or autobundled
             // summaries.
             final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
-                    (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
+                    (FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
                     mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
         }
@@ -2100,7 +2101,7 @@
             // Calling from user space, don't allow the canceling of actively
             // running foreground services.
             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
-                    pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
+                    pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
                     REASON_APP_CANCEL_ALL, null);
         }
 
@@ -2686,7 +2687,7 @@
         private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
                 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
-                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+                    Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
                     true,
                     userId, REASON_LISTENER_CANCEL, info);
         }
@@ -3963,7 +3964,7 @@
             // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
             // initially *and* force remove FLAG_FOREGROUND_SERVICE.
             sbn.getNotification().flags =
-                    (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
+                    (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
             mRankingHelper.sort(mNotificationList);
             mListeners.notifyPostedLocked(r, r);
         }
@@ -4049,7 +4050,7 @@
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
         r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));
 
-        if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
+        if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0
                 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
                 && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
             // Increase the importance of foreground service notifications unless the user had an
@@ -4430,7 +4431,7 @@
                         mUsageStats.registerUpdatedByApp(r, old);
                         // Make sure we don't lose the foreground service state.
                         notification.flags |=
-                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+                                old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
                         r.isUpdate = true;
                         r.setInterruptive(isVisuallyInterruptive(old, r));
                     }
@@ -4439,7 +4440,7 @@
 
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
-                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+                    if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
                         notification.flags |= Notification.FLAG_ONGOING_EVENT
                                 | Notification.FLAG_NO_CLEAR;
                     }
@@ -4507,6 +4508,13 @@
         if (oldN.extras == null || newN.extras == null) {
             return false;
         }
+
+        // Ignore visual interruptions from foreground services because users
+        // consider them one 'session'. Count them for everything else.
+        if (r != null && (r.sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
+            return false;
+        }
+
         if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
                 newN.extras.get(Notification.EXTRA_TITLE))) {
             return true;
@@ -5810,7 +5818,7 @@
             final StatusBarNotification childSbn = childR.sbn;
             if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
                     childR.getGroupKey().equals(parentNotification.getGroupKey())
-                    && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
+                    && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
                     && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
                         childSbn.getTag(), userId, 0, 0, reason, listenerName);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 3b8a994..fd51be5 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -694,32 +694,25 @@
     private final class OverlayChangeListener
             implements OverlayManagerServiceImpl.OverlayChangeListener {
         @Override
-        public void onChanged(@NonNull final String targetPackageName, final int userId,
-                boolean targetChanged, boolean overlayChanged) {
+        public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
             schedulePersistSettings();
             FgThread.getHandler().post(() -> {
-                // Update the targets' overlays if a change to the target or an overlay occurs
-                if (targetChanged || overlayChanged) {
-                    updateAssets(userId, targetPackageName);
+                updateAssets(userId, targetPackageName);
+
+                final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
+                        Uri.fromParts("package", targetPackageName, null));
+                intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+                if (DEBUG) {
+                    Slog.d(TAG, "send broadcast " + intent);
                 }
 
-                // Create the broadcast if the overlay changes
-                if (overlayChanged) {
-                    final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
-                            Uri.fromParts("package", targetPackageName, null));
-                    intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
-                    if (DEBUG) {
-                        Slog.d(TAG, "send broadcast " + intent);
-                    }
-
-                    try {
-                        ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
-                                null, null, null, android.app.AppOpsManager.OP_NONE, null, false,
-                                false, userId);
-                    } catch (RemoteException e) {
-                        // Intentionally left empty.
-                    }
+                try {
+                    ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
+                            null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
+                            userId);
+                } catch (RemoteException e) {
+                    // Intentionally left empty.
                 }
             });
         }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index a487ae9..c57f97b 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -207,7 +207,9 @@
             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -215,7 +217,9 @@
             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
@@ -224,7 +228,9 @@
                     + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING);
+        if (updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
@@ -232,7 +238,9 @@
             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -240,17 +248,21 @@
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, 0);
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     /**
-     * Calls OverlayChangeListener#onChanged if the settings for the overlay target were modified,
-     * and calls OverlayChangeListener#onTargetChanged to signal a change in the target package that
-     * requires updating target overlays.
+     * Update the state of any overlays for this target.
+     *
+     * Returns true if the system should refresh the app's overlay paths (i.e.
+     * if the settings were modified for this target, or there is at least one
+     * enabled framework overlay).
      */
-    private void updateAllOverlaysForTarget(@NonNull final String targetPackageName,
+    private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
             final int userId, final int flags) {
-        boolean overlayModified = false;
+        boolean modified = false;
         final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
         final int N = ois.size();
         for (int i = 0; i < N; i++) {
@@ -258,19 +270,22 @@
             final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
                     userId);
             if (overlayPackage == null) {
-                overlayModified |= mSettings.remove(oi.packageName, oi.userId);
+                modified |= mSettings.remove(oi.packageName, oi.userId);
                 removeIdmapIfPossible(oi);
             } else {
                 try {
-                    overlayModified |= updateState(targetPackageName, oi.packageName, userId, flags);
+                    modified |= updateState(targetPackageName, oi.packageName, userId, flags);
                 } catch (OverlayManagerSettings.BadKeyException e) {
                     Slog.e(TAG, "failed to update settings", e);
-                    overlayModified |= mSettings.remove(oi.packageName, userId);
+                    modified |= mSettings.remove(oi.packageName, userId);
                 }
             }
         }
 
-        mListener.onChanged(targetPackageName, userId, /* targetChanged */ true, overlayModified);
+        // check for enabled framework overlays
+        modified = modified || !getEnabledOverlayPackageNames("android", userId).isEmpty();
+
+        return modified;
     }
 
     void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
@@ -291,8 +306,7 @@
                 overlayPackage.overlayCategory);
         try {
             if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
-                mListener.onChanged(overlayPackage.overlayTarget, userId,
-                        /* targetChanged */ false,  /* overlayChanged */ true);
+                mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
             }
         } catch (OverlayManagerSettings.BadKeyException e) {
             Slog.e(TAG, "failed to update settings", e);
@@ -308,8 +322,7 @@
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (updateState(oi.targetPackageName, packageName, userId, 0)) {
-                mListener.onChanged(oi.targetPackageName, userId,
-                        /* targetChanged */ false,  /* overlayChanged */ true);
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
             }
         } catch (OverlayManagerSettings.BadKeyException e) {
             Slog.e(TAG, "failed to update settings", e);
@@ -326,8 +339,7 @@
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_UPGRADING)) {
                 removeIdmapIfPossible(oi);
-                mListener.onChanged(oi.targetPackageName, userId,
-                        /* targetChanged */ false,  /* overlayChanged */ true);
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
             }
         } catch (OverlayManagerSettings.BadKeyException e) {
             Slog.e(TAG, "failed to update settings", e);
@@ -361,8 +373,7 @@
             }
 
             if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
-                mListener.onChanged(pkg.overlayTarget, userId,
-                        /* targetChanged */ false,  /* overlayChanged */ true);
+                mListener.onOverlaysChanged(pkg.overlayTarget, userId);
             }
         } catch (OverlayManagerSettings.BadKeyException e) {
             Slog.e(TAG, "failed to update settings", e);
@@ -376,8 +387,7 @@
                 removeIdmapIfPossible(overlayInfo);
                 if (overlayInfo.isEnabled()) {
                     // Only trigger updates if the overlay was enabled.
-                    mListener.onChanged(overlayInfo.targetPackageName, userId,
-                            /* targetChanged */ false,  /* overlayChanged */ true);
+                    mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
                 }
             }
         } catch (OverlayManagerSettings.BadKeyException e) {
@@ -425,8 +435,7 @@
             modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
 
             if (modified) {
-                mListener.onChanged(oi.targetPackageName, userId,
-                        /* targetChanged */ false,  /* overlayChanged */ true);
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
             }
             return true;
         } catch (OverlayManagerSettings.BadKeyException e) {
@@ -485,8 +494,7 @@
             modified |= updateState(targetPackageName, packageName, userId, 0);
 
             if (modified) {
-                mListener.onChanged(targetPackageName, userId,
-                        /* targetChanged */ false,  /* overlayChanged */ true);
+                mListener.onOverlaysChanged(targetPackageName, userId);
             }
             return true;
         } catch (OverlayManagerSettings.BadKeyException e) {
@@ -519,8 +527,7 @@
         }
 
         if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
-            mListener.onChanged(overlayPackage.overlayTarget, userId,
-                    /* targetChanged */ false,  /* overlayChanged */ true);
+            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
         }
         return true;
     }
@@ -540,8 +547,7 @@
         }
 
         if (mSettings.setHighestPriority(packageName, userId)) {
-            mListener.onChanged(overlayPackage.overlayTarget, userId,
-                    /* targetChanged */ false,  /* overlayChanged */ true);
+            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
         }
         return true;
     }
@@ -561,8 +567,7 @@
         }
 
         if (mSettings.setLowestPriority(packageName, userId)) {
-            mListener.onChanged(overlayPackage.overlayTarget, userId,
-                    /* targetChanged */ false,  /* overlayChanged */ true);
+            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
         }
         return true;
     }
@@ -693,8 +698,7 @@
     }
 
     interface OverlayChangeListener {
-        void onChanged(@NonNull String targetPackage, int userId,
-                boolean targetChanged, boolean overlayChanged);
+        void onOverlaysChanged(@NonNull String targetPackage, int userId);
     }
 
     interface PackageManagerHelper {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2b4f34e..ad0ca6e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4227,6 +4227,11 @@
                     || appId == Process.ROOT_UID) {
                 return false;
             }
+            // Installer gets to see all static libs.
+            if (PackageManager.PERMISSION_GRANTED
+                    == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
+                return false;
+            }
         }
 
         // No package means no static lib as it is always on internal storage
@@ -8428,7 +8433,7 @@
 
                 // Delete invalid userdata apps
                 if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
-                        errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {
+                        errorCode != PackageManager.INSTALL_SUCCEEDED) {
                     logCriticalInfo(Log.WARN,
                             "Deleting invalid package at " + parseResult.scanFile);
                     removeCodePathLI(parseResult.scanFile);
@@ -22472,9 +22477,13 @@
         }
         final String volumeUuid = pkg.volumeUuid;
         final String packageName = pkg.packageName;
-        final ApplicationInfo app = (ps == null)
+
+        ApplicationInfo app = (ps == null)
                 ? pkg.applicationInfo
                 : PackageParser.generateApplicationInfo(pkg, 0, ps.readUserState(userId), userId);
+        if (app == null) {
+            app = pkg.applicationInfo;
+        }
 
         final int appId = UserHandle.getAppId(app.uid);
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a35b193..9af02a1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -234,6 +234,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayCutout;
+import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -6094,7 +6095,7 @@
         // Cancel any pending remote recents animations before handling the button itself. In the
         // case where we are going home and the recents animation has already started, just cancel
         // the recents animation, leaving the home stack in place for the pending start activity
-        if (isNavBarVirtKey && !down) {
+        if (isNavBarVirtKey && !down && !canceled) {
             boolean isHomeKey = keyCode == KeyEvent.KEYCODE_HOME;
             mActivityManagerInternal.cancelRecentsAnimation(!isHomeKey);
         }
@@ -7191,14 +7192,35 @@
     }
 
     @Override
-    public boolean isDockSideAllowed(int dockSide) {
+    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+            int displayHeight, int displayRotation) {
+        final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+        return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove);
+    }
 
-        // We do not allow all dock sides at which the navigation bar touches the docked stack.
-        if (!mNavigationBarCanMove) {
-            return dockSide == DOCKED_TOP || dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT;
-        } else {
-            return dockSide == DOCKED_TOP || dockSide == DOCKED_LEFT;
+    @VisibleForTesting
+    static boolean isDockSideAllowed(int dockSide, int originalDockSide,
+            int navBarPosition, boolean navigationBarCanMove) {
+        if (dockSide == DOCKED_TOP) {
+            return true;
         }
+
+        if (navigationBarCanMove) {
+            // Only allow the dockside opposite to the nav bar position in landscape
+            return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
+                    || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
+        }
+
+        // Side is the same as original side
+        if (dockSide == originalDockSide) {
+            return true;
+        }
+
+        // Only if original docked side was top in portrait will allow left for landscape
+        if (dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP) {
+            return true;
+        }
+        return false;
     }
 
     void sendCloseSystemWindows() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ccbf502..8690a83 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1706,11 +1706,19 @@
             DisplayCutout displayCutout, Rect outInsets);
 
     /**
+     * @param displayRotation the current display rotation
+     * @param displayWidth the current display width
+     * @param displayHeight the current display height
+     * @param dockSide the dockside asking if allowed
+     * @param originalDockSide the side that was original docked to in split screen
      * @return True if a specified {@param dockSide} is allowed on the current device, or false
      *         otherwise. It is guaranteed that at least one dock side for a particular orientation
      *         is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
+     *         If navigation bar is movable then the docked side would bias towards the
+     *         {@param originalDockSide}.
      */
-    public boolean isDockSideAllowed(int dockSide);
+    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+            int displayHeight, int displayRotation);
 
     /**
      * Called when the configuration has changed, and it's safe to load new values from resources.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 20283a7..a492672 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -719,13 +719,14 @@
      * Plays the wireless charging sound for both wireless and non-wireless charging
      */
     private void playChargingStartedSound() {
-        // TODO (b/77912907): add back charging sound enabled check & default to charging sounds ON
+        final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
         final boolean dndOff = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
                 == Settings.Global.ZEN_MODE_OFF;
         final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.CHARGING_STARTED_SOUND);
-        if (dndOff && soundPath != null) {
+        if (enabled && dndOff && soundPath != null) {
             final Uri soundUri = Uri.parse("file://" + soundPath);
             if (soundUri != null) {
                 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index c527533..cb84cf3 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -90,6 +90,16 @@
      */
     private final Plugin[] mPlugins;
 
+    public static final int REASON_AUTOMATIC_ON = 0;
+    public static final int REASON_AUTOMATIC_OFF = 1;
+    public static final int REASON_MANUAL_ON = 2;
+    public static final int REASON_MANUAL_OFF = 3;
+    public static final int REASON_STICKY_RESTORE = 4;
+    public static final int REASON_INTERACTIVE_CHANGED = 5;
+    public static final int REASON_POLICY_CHANGED = 6;
+    public static final int REASON_PLUGGED_IN = 7;
+    public static final int REASON_SETTING_CHANGED = 8;
+
     /**
      * Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
      */
@@ -113,7 +123,8 @@
                         return; // No need to send it if not enabled.
                     }
                     // Don't send the broadcast, because we never did so in this case.
-                    mHandler.postStateChanged(/*sendBroadcast=*/ false);
+                    mHandler.postStateChanged(/*sendBroadcast=*/ false,
+                            REASON_INTERACTIVE_CHANGED);
                     break;
                 case Intent.ACTION_BATTERY_CHANGED:
                     synchronized (mLock) {
@@ -184,7 +195,7 @@
         if (!isEnabled()) {
             return; // No need to send it if not enabled.
         }
-        mHandler.postStateChanged(/*sendBroadcast=*/ true);
+        mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED);
     }
 
     private class MyHandler extends Handler {
@@ -199,9 +210,9 @@
             super(looper);
         }
 
-        public void postStateChanged(boolean sendBroadcast) {
+        public void postStateChanged(boolean sendBroadcast, int reason) {
             obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
-                    ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
+                    ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget();
         }
 
         public void postSystemReady() {
@@ -212,7 +223,9 @@
         public void dispatchMessage(Message msg) {
             switch (msg.what) {
                 case MSG_STATE_CHANGED:
-                    handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
+                    handleBatterySaverStateChanged(
+                            msg.arg1 == ARG_SEND_BROADCAST,
+                            msg.arg2);
                     break;
 
                 case MSG_SYSTEM_READY:
@@ -227,14 +240,14 @@
     /**
      * Called by {@link PowerManagerService} to update the battery saver stete.
      */
-    public void enableBatterySaver(boolean enable) {
+    public void enableBatterySaver(boolean enable, int reason) {
         synchronized (mLock) {
             if (mEnabled == enable) {
                 return;
             }
             mEnabled = enable;
 
-            mHandler.postStateChanged(/*sendBroadcast=*/ true);
+            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
         }
     }
 
@@ -275,7 +288,7 @@
      * - When battery saver is on the interactive state changes.
      * - When battery saver is on the battery saver policy changes.
      */
-    void handleBatterySaverStateChanged(boolean sendBroadcast) {
+    void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
         final LowPowerModeListener[] listeners;
 
         final boolean enabled;
@@ -287,7 +300,8 @@
                     mPreviouslyEnabled ? 1 : 0, // Previously off or on.
                     mEnabled ? 1 : 0, // Now off or on.
                     isInteractive ?  1 : 0, // Device interactive state.
-                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "");
+                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "",
+                    reason);
             mPreviouslyEnabled = mEnabled;
 
             listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index 2860521..b9f31b1 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -18,6 +18,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
@@ -27,6 +28,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
 import com.android.server.power.BatterySaverPolicy;
 import com.android.server.power.BatterySaverStateMachineProto;
 
@@ -95,6 +97,18 @@
     @GuardedBy("mLock")
     private boolean mBatterySaverSnoozing;
 
+    /**
+     * Last reason passed to {@link #enableBatterySaverLocked}.
+     */
+    @GuardedBy("mLock")
+    private int mLastChangedIntReason;
+
+    /**
+     * Last reason passed to {@link #enableBatterySaverLocked}.
+     */
+    @GuardedBy("mLock")
+    private String mLastChangedStrReason;
+
     private final ContentObserver mSettingsObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean selfChange) {
@@ -149,11 +163,25 @@
         });
     }
 
+    /**
+     * Run a {@link Runnable} on a background handler.
+     */
     @VisibleForTesting
     void runOnBgThread(Runnable r) {
         BackgroundThread.getHandler().post(r);
     }
 
+    /**
+     * Run a {@link Runnable} on a background handler, but lazily. If the same {@link Runnable},
+     * it'll be first removed before a new one is posted.
+     */
+    @VisibleForTesting
+    void runOnBgThreadLazy(Runnable r, int delayMillis) {
+        final Handler h = BackgroundThread.getHandler();
+        h.removeCallbacks(r);
+        h.postDelayed(r, delayMillis);
+    }
+
     void refreshSettingsLocked() {
         final ContentResolver cr = mContext.getContentResolver();
 
@@ -199,14 +227,23 @@
         mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
         mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
 
+        if (thresholdChanged) {
+            // To avoid spamming the event log, we throttle logging here.
+            runOnBgThreadLazy(mThresholdChangeLogger, 2000);
+        }
+
         if (enabledChanged) {
             final String reason = batterySaverEnabled
                     ? "Global.low_power changed to 1" : "Global.low_power changed to 0";
             enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true,
-                    reason);
+                    BatterySaverController.REASON_SETTING_CHANGED, reason);
         }
     }
 
+    private final Runnable mThresholdChangeLogger = () -> {
+        EventLogTags.writeBatterySaverSetting(mSettingBatterySaverTriggerThreshold);
+    };
+
     /**
      * {@link com.android.server.power.PowerManagerService} calls it when battery state changes.
      *
@@ -257,18 +294,26 @@
         }
         if (mIsPowered) {
             updateSnoozingLocked(false, "Plugged in");
-            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Plugged in");
+            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+                    BatterySaverController.REASON_PLUGGED_IN,
+                    "Plugged in");
 
         } else if (mSettingBatterySaverEnabledSticky) {
             // Re-enable BS.
-            enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, "Sticky restore");
+            enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
+                    BatterySaverController.REASON_STICKY_RESTORE,
+                    "Sticky restore");
 
         } else if (mIsBatteryLevelLow) {
             if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
-                enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, "Auto ON");
+                enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
+                        BatterySaverController.REASON_AUTOMATIC_ON,
+                        "Auto ON");
             }
         } else { // Battery not low
-            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, "Auto OFF");
+            enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+                    BatterySaverController.REASON_AUTOMATIC_OFF,
+                    "Auto OFF");
         }
     }
 
@@ -284,6 +329,8 @@
         }
         synchronized (mLock) {
             enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,
+                    (enabled ? BatterySaverController.REASON_MANUAL_ON
+                            : BatterySaverController.REASON_MANUAL_OFF),
                     (enabled ? "Manual ON" : "Manual OFF"));
         }
     }
@@ -292,10 +339,11 @@
      * Actually enable / disable battery saver. Write the new state to the global settings
      * and propagate it to {@link #mBatterySaverController}.
      */
-    private void enableBatterySaverLocked(boolean enable, boolean manual, String reason) {
+    private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
+            String strReason) {
         if (DEBUG) {
             Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
-                    + " reason=" + reason);
+                    + " reason=" + strReason + "(" + intReason + ")");
         }
         final boolean wasEnabled = mBatterySaverController.isEnabled();
 
@@ -309,6 +357,8 @@
             if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
             return;
         }
+        mLastChangedIntReason = intReason;
+        mLastChangedStrReason = strReason;
 
         if (manual) {
             if (enable) {
@@ -330,12 +380,12 @@
             mSettingBatterySaverEnabledSticky = enable;
             putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
         }
-        mBatterySaverController.enableBatterySaver(enable);
+        mBatterySaverController.enableBatterySaver(enable, intReason);
 
         if (DEBUG) {
             Slog.d(TAG, "Battery saver: Enabled=" + enable
                     + " manual=" + manual
-                    + " reason=" + reason);
+                    + " reason=" + strReason + "(" + intReason + ")");
         }
     }
 
@@ -365,6 +415,11 @@
             pw.print("  Enabled=");
             pw.println(mBatterySaverController.isEnabled());
 
+            pw.print("  mLastChangedIntReason=");
+            pw.println(mLastChangedIntReason);
+            pw.print("  mLastChangedStrReason=");
+            pw.println(mLastChangedStrReason);
+
             pw.print("  mBootCompleted=");
             pw.println(mBootCompleted);
             pw.print("  mSettingsLoaded=");
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index a9cdafd..507f0a8 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -155,11 +155,10 @@
         enforceAccess(pkg, uri);
         int user = Binder.getCallingUserHandle().getIdentifier();
         uri = maybeAddUserId(uri, user);
-        getOrCreatePinnedSlice(uri, pkg).pin(pkg, specs, token);
+        String slicePkg = getProviderPkg(uri, user);
+        getOrCreatePinnedSlice(uri, slicePkg).pin(pkg, specs, token);
 
-        Uri finalUri = uri;
         mHandler.post(() -> {
-            String slicePkg = getProviderPkg(finalUri, user);
             if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
                 mAppUsageStats.reportEvent(slicePkg, user,
                         isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c761754..2f174c2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -984,6 +984,7 @@
                 (height - thumbnailSize) / 2,
                 (width + thumbnailSize) / 2,
                 (height + thumbnailSize) / 2);
+        drawable.setTint(mContext.getColor(android.R.color.white));
         drawable.draw(canvas);
         picture.endRecording();
 
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 165a409..644e3c3 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -671,6 +671,17 @@
         }
     }
 
+    public void notifyAppStopping() {
+        synchronized(mWindowMap) {
+            if (mContainer == null) {
+                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+                        + mToken);
+                return;
+            }
+            mContainer.detachChildren();
+        }
+    }
+
     public void notifyAppStopped() {
         synchronized(mWindowMap) {
             if (mContainer == null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a701d42..966ca41 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -913,12 +913,16 @@
         // try and clean up it's child surfaces. We need to prevent this from
         // happening, so we sever the children, transfering their ownership
         // from the client it-self to the parent surface (owned by us).
+        detachChildren();
+
+        mPendingRelaunchCount++;
+    }
+
+    void detachChildren() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final WindowState w = mChildren.get(i);
             w.mWinAnimator.detachChildren();
         }
-
-        mPendingRelaunchCount++;
     }
 
     void finishRelaunching() {
@@ -1984,7 +1988,7 @@
         final Rect frame = win.mFrame;
         final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
                 ? R.drawable.ic_account_circle
-                : R.drawable.ic_corp_badge_no_background;
+                : R.drawable.ic_corp_badge;
         final GraphicBuffer thumbnail =
                 mService.mAppTransition
                         .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 112d93c..b2a12be 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -401,12 +401,13 @@
                 + " replacing=" + replacing);
 
         if (replacing) {
-            if (existing.isAnimatingTo(to)) {
+            if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen)
+                    && (!moveFromFullscreen || existing.mMoveFromFullscreen)) {
                 // Just let the current animation complete if it has the same destination as the
-                // one we are trying to start.
-                if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
-                        + " ignoring...");
-
+                // one we are trying to start, and, if moveTo/FromFullscreen was requested, already
+                // has that flag set.
+                if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as "
+                        + "existing=" + existing + ", ignoring...");
                 return existing;
             }
 
@@ -434,6 +435,13 @@
                 }
             }
 
+            // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation
+            // specifies a direction.
+            if (!moveFromFullscreen && !moveToFullscreen) {
+                moveToFullscreen = existing.mMoveToFullscreen;
+                moveFromFullscreen = existing.mMoveFromFullscreen;
+            }
+
             // Since we are replacing, we skip both animation start and end callbacks
             existing.cancel();
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4fd31ff..38fb30e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3906,7 +3906,7 @@
      * with {@link #WindowState#assignLayer}
      */
     void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
-        t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+        child.assignRelativeLayer(t, mImeWindowsContainers.getSurfaceControl(), 1);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 2cd2ef1..c8baced 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -170,7 +170,7 @@
             final int orientation = mTmpRect2.width() <= mTmpRect2.height()
                     ? ORIENTATION_PORTRAIT
                     : ORIENTATION_LANDSCAPE;
-            final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation);
+            final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation);
             final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
                     getContentWidth());
 
@@ -191,6 +191,39 @@
         return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
     }
 
+    /**
+     * Get the current docked side. Determined by its location of {@param bounds} within
+     * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+     * if allowed in its respected {@param orientation}.
+     *
+     * @param bounds bounds of the docked task to get which side is docked
+     * @param displayRect bounds of the display that contains the docked task
+     * @param orientation the origination of device
+     * @return current docked side
+     */
+    int getDockSide(Rect bounds, Rect displayRect, int orientation) {
+        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            // Portrait mode, docked either at the top or the bottom.
+            final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+            if (diff > 0) {
+                return DOCKED_TOP;
+            } else if (diff < 0) {
+                return DOCKED_BOTTOM;
+            }
+            return canPrimaryStackDockTo(DOCKED_TOP) ? DOCKED_TOP : DOCKED_BOTTOM;
+        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            // Landscape mode, docked either on the left or on the right.
+            final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+            if (diff > 0) {
+                return DOCKED_LEFT;
+            } else if (diff < 0) {
+                return DOCKED_RIGHT;
+            }
+            return canPrimaryStackDockTo(DOCKED_LEFT) ? DOCKED_LEFT : DOCKED_RIGHT;
+        }
+        return DOCKED_INVALID;
+    }
+
     void getHomeStackBoundsInDockedMode(Rect outBounds) {
         final DisplayInfo di = mDisplayContent.getDisplayInfo();
         mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
@@ -203,10 +236,20 @@
             outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
                     di.logicalWidth, di.logicalHeight);
         } else {
-            // In landscape append the left position with the statusbar height to match the
+            // In landscape also inset the left/right side with the statusbar height to match the
             // minimized size height in portrait mode.
-            outBounds.set(mTaskHeightInMinimizedMode + dividerSize + mTmpRect.left + mTmpRect.top,
-                    0, di.logicalWidth, di.logicalHeight);
+            final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+            final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
+            int left = mTmpRect.left;
+            int right = di.logicalWidth - mTmpRect.right;
+            if (stack != null) {
+                if (stack.getDockSide() == DOCKED_LEFT) {
+                    left += primaryTaskWidth;
+                } else if (stack.getDockSide() == DOCKED_RIGHT) {
+                    right -= primaryTaskWidth;
+                }
+            }
+            outBounds.set(left, 0, right, di.logicalHeight);
         }
     }
 
@@ -420,21 +463,9 @@
      * @return true if the side provided is valid
      */
     boolean canPrimaryStackDockTo(int dockSide) {
-        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
-            // Side is the same as original side
-            if (dockSide == mOriginalDockedSide) {
-                return true;
-            }
-            // Special rule that the top in portrait is always valid
-            if (dockSide == DOCKED_TOP) {
-                return true;
-            }
-            // Only if original docked side was top in portrait will allow left side for landscape
-            if (dockSide == DOCKED_LEFT && mOriginalDockedSide == DOCKED_TOP) {
-                return true;
-            }
-        }
-        return false;
+        final DisplayInfo di = mDisplayContent.getDisplayInfo();
+        return mService.mPolicy.isDockSideAllowed(dockSide, mOriginalDockedSide, di.logicalWidth,
+                di.logicalHeight, di.rotation);
     }
 
     void notifyDockedStackExistsChanged(boolean exists) {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index ddb67b4..653850a 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -244,12 +245,15 @@
     }
 
     /**
-     * Adjusts the screen size in dp's for the {@param config} for the given params.
+     * Adjusts the screen size in dp's for the {@param config} for the given params. The provided
+     * params represent the desired state of a configuration change. Since this utility is used
+     * before mContainer has been updated, any relevant properties (like {@param windowingMode})
+     * need to be passed in.
      */
     public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
             Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
             boolean overrideHeight, float density, Configuration config,
-            Configuration parentConfig) {
+            Configuration parentConfig, int windowingMode) {
         synchronized (mWindowMap) {
             final TaskStack stack = mContainer;
             final DisplayContent displayContent = stack.getDisplayContent();
@@ -272,10 +276,10 @@
             config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null);
             boolean intersectParentBounds = false;
 
-            if (stack.getWindowConfiguration().tasksAreFloating()) {
+            if (WindowConfiguration.isFloating(windowingMode)) {
                 // Floating tasks should not be resized to the screen's bounds.
 
-                if (stack.inPinnedWindowingMode()
+                if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED
                         && bounds.width() == mTmpDisplayBounds.width()
                         && bounds.height() == mTmpDisplayBounds.height()) {
                     // If the bounds we are animating is the same as the fullscreen stack
@@ -316,7 +320,7 @@
             config.screenWidthDp = width;
             config.screenHeightDp = height;
             config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
-                    insetBounds != null ? insetBounds : bounds, density);
+                    insetBounds != null ? insetBounds : bounds, density, windowingMode);
         }
     }
 
@@ -338,11 +342,12 @@
     }
 
     /**
-     * Calculates the smallest width for a task given the {@param bounds}.
+     * Calculates the smallest width for a task given the target {@param bounds} and
+     * {@param windowingMode}. Avoid using values from mContainer since they can be out-of-date.
      *
      * @return the smallest width to be used in the Configuration, in dips
      */
-    private int getSmallestWidthForTaskBounds(Rect bounds, float density) {
+    private int getSmallestWidthForTaskBounds(Rect bounds, float density, int windowingMode) {
         final DisplayContent displayContent = mContainer.getDisplayContent();
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
 
@@ -350,7 +355,7 @@
                 bounds.height() == displayInfo.logicalHeight)) {
             // If the bounds are fullscreen, return the value of the fullscreen configuration
             return displayContent.getConfiguration().smallestScreenWidthDp;
-        } else if (mContainer.getWindowConfiguration().tasksAreFloating()) {
+        } else if (WindowConfiguration.isFloating(windowingMode)) {
             // For floating tasks, calculate the smallest width from the bounds of the task
             return (int) (Math.min(bounds.width(), bounds.height()) / density);
         } else {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6b13edd..efc4e73 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -317,7 +317,7 @@
     @VisibleForTesting
     int getSnapshotMode(Task task) {
         final AppWindowToken topChild = task.getTopChild();
-        if (!task.isActivityTypeStandardOrUndefined()) {
+        if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
             return SNAPSHOT_MODE_NONE;
         } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
             return SNAPSHOT_MODE_APP_THEME;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 018765d..891ee2e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1462,27 +1462,7 @@
         }
         dc.getBounds(mTmpRect);
         final int orientation = dc.getConfiguration().orientation;
-        return getDockSideUnchecked(bounds, mTmpRect, orientation);
-    }
-
-    static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) {
-        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-            // Portrait mode, docked either at the top or the bottom.
-            if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) {
-                return DOCKED_TOP;
-            } else {
-                return DOCKED_BOTTOM;
-            }
-        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            // Landscape mode, docked either on the left or on the right.
-            if (bounds.left - displayRect.left <= displayRect.right - bounds.right) {
-                return DOCKED_LEFT;
-            } else {
-                return DOCKED_RIGHT;
-            }
-        } else {
-            return DOCKED_INVALID;
-        }
+        return dc.getDockedDividerController().getDockSide(bounds, mTmpRect, orientation);
     }
 
     boolean hasTaskForUser(int userId) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 04554ef..10dfdf2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7489,7 +7489,7 @@
 
     boolean hasWideColorGamutSupport() {
         return mHasWideColorGamutSupport &&
-                !SystemProperties.getBoolean("persist.sys.sf.native_mode", false);
+                SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1;
     }
 
     void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d5ebfb6..1021f15 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3865,9 +3865,13 @@
         windowInfo.title = mAttrs.accessibilityTitle;
         // Panel windows have no public way to set the a11y title directly. Use the
         // regular title as a fallback.
-        if (TextUtils.isEmpty(windowInfo.title)
-                && (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
-                && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)) {
+        final boolean isPanelWindow = (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
+                && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW);
+        // Accessibility overlays should have titles that work for accessibility, and can't set
+        // the a11y title themselves.
+        final boolean isAccessibilityOverlay =
+                windowInfo.type == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+        if (TextUtils.isEmpty(windowInfo.title) && (isPanelWindow || isAccessibilityOverlay)) {
             windowInfo.title = mAttrs.getTitle();
         }
         windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
@@ -4270,6 +4274,24 @@
         }
     }
 
+    private boolean skipDecorCrop() {
+        // The decor frame is used to specify the region not covered by the system
+        // decorations (nav bar, status bar). In case this is empty, for example with
+        // FLAG_TRANSLUCENT_NAVIGATION, we don't need to do any cropping.
+        if (mDecorFrame.isEmpty()) {
+            return true;
+        }
+
+        // But if we have a frame, and are an application window, then we must be cropped.
+        if (mAppToken != null) {
+            return false;
+        }
+
+        // For non application windows, we may be allowed to extend over the decor bars
+        // depending on our type and permissions assosciated with our token.
+        return mToken.canLayerAboveSystemBars();
+    }
+
     /**
      * Calculate the window crop according to system decor policy. In general this is
      * the system decor rect (see #calculateSystemDecorRect), but we also have some
@@ -4287,7 +4309,7 @@
             policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
                     displayInfo.logicalWidth - mCompatFrame.left,
                     displayInfo.logicalHeight - mCompatFrame.top);
-        } else if (mDecorFrame.isEmpty()) {
+        } else if (skipDecorCrop()) {
             // Windows without policy decor aren't cropped.
             policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
         } else {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 14680d9..b97460a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,7 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -336,4 +336,16 @@
     boolean okToAnimate() {
         return mDisplayContent != null && mDisplayContent.okToAnimate();
     }
+
+    /**
+     * Return whether windows from this token can layer above the
+     * system bars, or in other words extend outside of the "Decor Frame"
+     */
+    boolean canLayerAboveSystemBars() {
+        int layer = mService.mPolicy.getWindowLayerFromTypeLw(windowType,
+                mOwnerCanManageAppTokens);
+        int navLayer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
+                mOwnerCanManageAppTokens);
+        return mOwnerCanManageAppTokens && (layer > navLayer);
+    }
 }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index b3b37d6..a3a7e1e 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1854,7 +1854,7 @@
     return boolToJbool(result.isOk());
 }
 
-static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
+static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported(
         JNIEnv* env,
         jclass clazz) {
     if (gnssNavigationMessageIface != nullptr) {
@@ -1863,7 +1863,7 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GnssLocationProvider_start_navigation_message_collection(
+static jboolean android_location_GnssNavigationMessageProvider_start_navigation_message_collection(
         JNIEnv* env,
         jobject obj) {
     if (gnssNavigationMessageIface == nullptr) {
@@ -1884,7 +1884,7 @@
     return JNI_TRUE;
 }
 
-static jboolean android_location_GnssLocationProvider_stop_navigation_message_collection(
+static jboolean android_location_GnssNavigationMessageProvider_stop_navigation_message_collection(
         JNIEnv* env,
         jobject obj) {
     if (gnssNavigationMessageIface == nullptr) {
@@ -2178,18 +2178,6 @@
     {"native_update_network_state",
             "(ZIZZLjava/lang/String;Ljava/lang/String;)V",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_update_network_state)},
-    {"native_is_navigation_message_supported",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssLocationProvider_is_navigation_message_supported)},
-    {"native_start_navigation_message_collection",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssLocationProvider_start_navigation_message_collection)},
-    {"native_stop_navigation_message_collection",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssLocationProvider_stop_navigation_message_collection)},
     {"native_set_supl_es",
             "(I)Z",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_es)},
@@ -2273,6 +2261,22 @@
                     android_location_GnssMeasurementsProvider_stop_measurement_collection)},
 };
 
+static const JNINativeMethod sNavigationMessageMethods[] = {
+     /* name, signature, funcPtr */
+    {"native_is_navigation_message_supported",
+            "()Z",
+            reinterpret_cast<void *>(
+                    android_location_GnssNavigationMessageProvider_is_navigation_message_supported)},
+    {"native_start_navigation_message_collection",
+            "()Z",
+            reinterpret_cast<void *>(
+                    android_location_GnssNavigationMessageProvider_start_navigation_message_collection)},
+    {"native_stop_navigation_message_collection",
+            "()Z",
+            reinterpret_cast<void *>(
+                    android_location_GnssNavigationMessageProvider_stop_navigation_message_collection)},
+};
+
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
     jniRegisterNativeMethods(
             env,
@@ -2289,6 +2293,11 @@
             "com/android/server/location/GnssMeasurementsProvider",
             sMeasurementMethods,
             NELEM(sMeasurementMethods));
+    jniRegisterNativeMethods(
+            env,
+            "com/android/server/location/GnssNavigationMessageProvider",
+            sNavigationMessageMethods,
+            NELEM(sNavigationMessageMethods));
     return jniRegisterNativeMethods(
             env,
             "com/android/server/location/GnssLocationProvider",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7b6ebf4..70afbc3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -156,6 +156,9 @@
     // give any timezone code room without going into negative time.
     private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;
 
+    private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
+    private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
+
     /*
      * Implementation class names. TODO: Move them to a codegen class or load
      * them from the build system somehow.
@@ -396,6 +399,8 @@
                 android.os.Process.THREAD_PRIORITY_FOREGROUND);
             android.os.Process.setCanSelfBackground(false);
             Looper.prepareMainLooper();
+            Looper.getMainLooper().setSlowLogThresholdMs(
+                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
 
             // Initialize native services.
             System.loadLibrary("android_servers");
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
index 703b415..dec8ca2 100644
--- a/services/net/java/android/net/apf/ApfCapabilities.java
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -49,4 +49,14 @@
         return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
                 apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
     }
+
+    /**
+     * Returns true if the APF interpreter advertises support for the data buffer access opcodes
+     * LDDW and STDW.
+     *
+     * Full LDDW and STDW support is present from APFv4 on.
+     */
+    public boolean hasDataAccess() {
+        return apfVersionSupported >= 4;
+    }
 }
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 9a97ba7..2bf6e92 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -24,6 +24,7 @@
 import static com.android.internal.util.BitUtils.getUint8;
 import static com.android.internal.util.BitUtils.uint32;
 
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -102,6 +103,70 @@
         UPDATE_EXPIRY   // APF program updated for expiry
     }
 
+    /**
+     * APF packet counters.
+     *
+     * Packet counters are 32bit big-endian values, and allocated near the end of the APF data
+     * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4,
+     * the last writable 32bit word.
+     */
+    @VisibleForTesting
+    private static enum Counter {
+        RESERVED_OOB,  // Points to offset 0 from the end of the buffer (out-of-bounds)
+        TOTAL_PACKETS,
+        PASSED_ARP,
+        PASSED_DHCP,
+        PASSED_IPV4,
+        PASSED_IPV6_NON_ICMP,
+        PASSED_IPV4_UNICAST,
+        PASSED_IPV6_ICMP,
+        PASSED_IPV6_UNICAST_NON_ICMP,
+        PASSED_ARP_NON_IPV4,
+        PASSED_ARP_UNKNOWN,
+        PASSED_ARP_UNICAST_REPLY,
+        PASSED_NON_IP_UNICAST,
+        DROPPED_ETH_BROADCAST,
+        DROPPED_RA,
+        DROPPED_GARP_REPLY,
+        DROPPED_ARP_OTHER_HOST,
+        DROPPED_IPV4_L2_BROADCAST,
+        DROPPED_IPV4_BROADCAST_ADDR,
+        DROPPED_IPV4_BROADCAST_NET,
+        DROPPED_IPV4_MULTICAST,
+        DROPPED_IPV6_ROUTER_SOLICITATION,
+        DROPPED_IPV6_MULTICAST_NA,
+        DROPPED_IPV6_MULTICAST,
+        DROPPED_IPV6_MULTICAST_PING,
+        DROPPED_IPV6_NON_ICMP_MULTICAST,
+        DROPPED_802_3_FRAME,
+        DROPPED_ETHERTYPE_BLACKLISTED;
+
+        // Returns the negative byte offset from the end of the APF data segment for
+        // a given counter.
+        public int offset() {
+            return - this.ordinal() * 4;  // Currently, all counters are 32bit long.
+        }
+
+        // Returns the total size of the data segment in bytes.
+        public static int totalSize() {
+            return (Counter.class.getEnumConstants().length - 1) * 4;
+        }
+    }
+
+    /**
+     * When APFv4 is supported, loads R1 with the offset of the specified counter.
+     */
+    private void maybeSetCounter(ApfGenerator gen, Counter c) {
+        if (mApfCapabilities.hasDataAccess()) {
+            gen.addLoadImmediate(Register.R1, c.offset());
+        }
+    }
+
+    // When APFv4 is supported, these point to the trampolines generated by emitEpilogue().
+    // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL.
+    private final String mCountAndPassLabel;
+    private final String mCountAndDropLabel;
+
     // Thread to listen for RAs.
     @VisibleForTesting
     class ReceiveThread extends Thread {
@@ -289,6 +354,16 @@
         mDrop802_3Frames = config.ieee802_3Filter;
         mContext = context;
 
+        if (mApfCapabilities.hasDataAccess()) {
+            mCountAndPassLabel = "countAndPass";
+            mCountAndDropLabel = "countAndDrop";
+        } else {
+            // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
+            // preserving the original pre-APFv4 behavior.
+            mCountAndPassLabel = ApfGenerator.PASS_LABEL;
+            mCountAndDropLabel = ApfGenerator.DROP_LABEL;
+        }
+
         // Now fill the black list from the passed array
         mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
 
@@ -302,6 +377,10 @@
                 new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
     }
 
+    public synchronized void setDataSnapshot(byte[] data) {
+        mDataSnapshot = data;
+    }
+
     private void log(String s) {
         Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
     }
@@ -350,6 +429,10 @@
         try {
             mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
             synchronized(this) {
+                // Clear APF memory.
+                byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize];
+                mIpClientCallback.installPacketFilter(zeroes);
+
                 // Install basic filters
                 installNewProgramLocked();
             }
@@ -729,7 +812,8 @@
                     gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
                 }
             }
-            gen.addJump(gen.DROP_LABEL);
+            maybeSetCounter(gen, Counter.DROPPED_RA);
+            gen.addJump(mCountAndDropLabel);
             gen.defineLabel(nextFilterLabel);
             return filterLifetime;
         }
@@ -764,6 +848,16 @@
     @GuardedBy("this")
     private byte[] mLastInstalledProgram;
 
+    /**
+     * For debugging only. Contains the latest APF buffer snapshot captured from the firmware.
+     *
+     * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports
+     * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for
+     * the opcodes to access the data buffer (LDDW and STDW).
+     */
+    @GuardedBy("this") @Nullable
+    private byte[] mDataSnapshot;
+
     // How many times the program was updated since we started.
     @GuardedBy("this")
     private int mNumProgramUpdates = 0;
@@ -799,31 +893,37 @@
 
         // Pass if not ARP IPv4.
         gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
-        gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL);
+        maybeSetCounter(gen, Counter.PASSED_ARP_NON_IPV4);
+        gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
 
         // Pass if unknown ARP opcode.
         gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
         gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
-        gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL);
+        maybeSetCounter(gen, Counter.PASSED_ARP_UNKNOWN);
+        gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
 
         // Pass if unicast reply.
         gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
-        gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+        maybeSetCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
+        gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
 
         // Either a unicast request, a unicast reply, or a broadcast reply.
         gen.defineLabel(checkTargetIPv4);
         if (mIPv4Address == null) {
             // When there is no IPv4 address, drop GARP replies (b/29404209).
             gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
-            gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL);
+            maybeSetCounter(gen, Counter.DROPPED_GARP_REPLY);
+            gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
         } else {
             // When there is an IPv4 address, drop unicast/broadcast requests
             // and broadcast replies with a different target IPv4 address.
             gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
-            gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL);
+            maybeSetCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
+            gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
         }
 
-        gen.addJump(gen.PASS_LABEL);
+        maybeSetCounter(gen, Counter.PASSED_ARP);
+        gen.addJump(mCountAndPassLabel);
     }
 
     /**
@@ -866,7 +966,8 @@
             // NOTE: Relies on R1 containing IPv4 header offset.
             gen.addAddR1();
             gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
-            gen.addJump(gen.PASS_LABEL);
+            maybeSetCounter(gen, Counter.PASSED_DHCP);
+            gen.addJump(mCountAndPassLabel);
 
             // Drop all multicasts/broadcasts.
             gen.defineLabel(skipDhcpv4Filter);
@@ -874,24 +975,31 @@
             // If IPv4 destination address is in multicast range, drop.
             gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
             gen.addAnd(0xf0);
-            gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
+            maybeSetCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
+            gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
 
             // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
+            maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
             gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
-            gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, gen.DROP_LABEL);
+            gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
             if (mIPv4Address != null && mIPv4PrefixLength < 31) {
+                maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
                 int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
-                gen.addJumpIfR0Equals(broadcastAddr, gen.DROP_LABEL);
+                gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
             }
 
             // If L2 broadcast packet, drop.
+            // TODO: can we invert this condition to fall through to the common pass case below?
+            maybeSetCounter(gen, Counter.PASSED_IPV4_UNICAST);
             gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
-            gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
-            gen.addJump(gen.DROP_LABEL);
+            gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
+            maybeSetCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
+            gen.addJump(mCountAndDropLabel);
         }
 
         // Otherwise, pass
-        gen.addJump(gen.PASS_LABEL);
+        maybeSetCounter(gen, Counter.PASSED_IPV4);
+        gen.addJump(mCountAndPassLabel);
     }
 
 
@@ -938,14 +1046,17 @@
 
             // Drop all other packets sent to ff00::/8 (multicast prefix).
             gen.defineLabel(dropAllIPv6MulticastsLabel);
+            maybeSetCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
             gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
-            gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
+            gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
             // Not multicast. Pass.
-            gen.addJump(gen.PASS_LABEL);
+            maybeSetCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
+            gen.addJump(mCountAndPassLabel);
             gen.defineLabel(skipIPv6MulticastFilterLabel);
         } else {
             // If not ICMPv6, pass.
-            gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+            maybeSetCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
+            gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
         }
 
         // If we got this far, the packet is ICMPv6.  Drop some specific types.
@@ -954,7 +1065,8 @@
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
         gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
         // Drop all router solicitations (b/32833400)
-        gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL);
+        maybeSetCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
+        gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
         // If not neighbor announcements, skip filter.
         gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
         // If to ff02::1, drop.
@@ -962,7 +1074,8 @@
         gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
         gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
                 skipUnsolicitedMulticastNALabel);
-        gen.addJump(gen.DROP_LABEL);
+        maybeSetCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
+        gen.addJump(mCountAndDropLabel);
         gen.defineLabel(skipUnsolicitedMulticastNALabel);
     }
 
@@ -985,10 +1098,18 @@
      * </ul>
      */
     @GuardedBy("this")
-    private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
+    private ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
         // This is guaranteed to succeed because of the check in maybeCreate.
         ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
 
+        if (mApfCapabilities.hasDataAccess()) {
+            // Increment TOTAL_PACKETS
+            maybeSetCounter(gen, Counter.TOTAL_PACKETS);
+            gen.addLoadData(Register.R0, 0);  // load counter
+            gen.addAdd(1);
+            gen.addStoreData(Register.R0, 0);  // write-back counter
+        }
+
         // Here's a basic summary of what the initial program does:
         //
         // if it's a 802.3 Frame (ethtype < 0x0600):
@@ -1009,12 +1130,14 @@
 
         if (mDrop802_3Frames) {
             // drop 802.3 frames (ethtype < 0x0600)
-            gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL);
+            maybeSetCounter(gen, Counter.DROPPED_802_3_FRAME);
+            gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
         }
 
         // Handle ether-type black list
+        maybeSetCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
         for (int p : mEthTypeBlackList) {
-            gen.addJumpIfR0Equals(p, gen.DROP_LABEL);
+            gen.addJumpIfR0Equals(p, mCountAndDropLabel);
         }
 
         // Add ARP filters:
@@ -1041,8 +1164,10 @@
 
         // Drop non-IP non-ARP broadcasts, pass the rest
         gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
-        gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
-        gen.addJump(gen.DROP_LABEL);
+        maybeSetCounter(gen, Counter.PASSED_NON_IP_UNICAST);
+        gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
+        maybeSetCounter(gen, Counter.DROPPED_ETH_BROADCAST);
+        gen.addJump(mCountAndDropLabel);
 
         // Add IPv6 filters:
         gen.defineLabel(ipv6FilterLabel);
@@ -1051,6 +1176,39 @@
     }
 
     /**
+     * Append packet counting epilogue to the APF program.
+     *
+     * Currently, the epilogue consists of two trampolines which count passed and dropped packets
+     * before jumping to the actual PASS and DROP labels.
+     */
+    @GuardedBy("this")
+    private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
+        // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
+        // will just fall-through to the PASS label.
+        if (!mApfCapabilities.hasDataAccess()) return;
+
+        // Execution will reach the bottom of the program if none of the filters match,
+        // which will pass the packet to the application processor.
+        maybeSetCounter(gen, Counter.PASSED_IPV6_ICMP);
+
+        // Append the count & pass trampoline, which increments the counter at the data address
+        // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
+        // the entire sequence inline for every counter.
+        gen.defineLabel(mCountAndPassLabel);
+        gen.addLoadData(Register.R0, 0);   // R0 = *(R1 + 0)
+        gen.addAdd(1);                     // R0++
+        gen.addStoreData(Register.R0, 0);  // *(R1 + 0) = R0
+        gen.addJump(gen.PASS_LABEL);
+
+        // Same as above for the count & drop trampoline.
+        gen.defineLabel(mCountAndDropLabel);
+        gen.addLoadData(Register.R0, 0);   // R0 = *(R1 + 0)
+        gen.addAdd(1);                     // R0++
+        gen.addStoreData(Register.R0, 0);  // *(R1 + 0) = R0
+        gen.addJump(gen.DROP_LABEL);
+    }
+
+    /**
      * Generate and install a new filter program.
      */
     @GuardedBy("this")
@@ -1060,22 +1218,39 @@
         ArrayList<Ra> rasToFilter = new ArrayList<>();
         final byte[] program;
         long programMinLifetime = Long.MAX_VALUE;
+        long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize;
+        if (mApfCapabilities.hasDataAccess()) {
+            // Reserve space for the counters.
+            maximumApfProgramSize -= Counter.totalSize();
+        }
+
         try {
             // Step 1: Determine how many RA filters we can fit in the program.
-            ApfGenerator gen = beginProgramLocked();
+            ApfGenerator gen = emitPrologueLocked();
+
+            // The epilogue normally goes after the RA filters, but add it early to include its
+            // length when estimating the total.
+            emitEpilogue(gen);
+
+            // Can't fit the program even without any RA filters?
+            if (gen.programLengthOverEstimate() > maximumApfProgramSize) {
+                Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize);
+                return;
+            }
+
             for (Ra ra : mRas) {
                 ra.generateFilterLocked(gen);
                 // Stop if we get too big.
-                if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break;
+                if (gen.programLengthOverEstimate() > maximumApfProgramSize) break;
                 rasToFilter.add(ra);
             }
+
             // Step 2: Actually generate the program
-            gen = beginProgramLocked();
+            gen = emitPrologueLocked();
             for (Ra ra : rasToFilter) {
                 programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
             }
-            // Execution will reach the end of the program if no filters match, which will pass the
-            // packet to the AP.
+            emitEpilogue(gen);
             program = gen.generate();
         } catch (IllegalInstructionException|IllegalStateException e) {
             Log.e(TAG, "Failed to generate APF program.", e);
@@ -1278,6 +1453,23 @@
         installNewProgramLocked();
     }
 
+    static public long counterValue(byte[] data, Counter counter)
+            throws ArrayIndexOutOfBoundsException {
+        // Follow the same wrap-around addressing scheme of the interpreter.
+        int offset = counter.offset();
+        if (offset < 0) {
+            offset = data.length + offset;
+        }
+
+        // Decode 32bit big-endian integer into a long so we can count up beyond 2^31.
+        long value = 0;
+        for (int i = 0; i < 4; i++) {
+            value = value << 8 | (data[offset] & 0xFF);
+            offset++;
+        }
+        return value;
+    }
+
     public synchronized void dump(IndentingPrintWriter pw) {
         pw.println("Capabilities: " + mApfCapabilities);
         pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
@@ -1319,6 +1511,32 @@
             pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
             pw.decreaseIndent();
         }
+
+        pw.println("APF packet counters: ");
+        pw.increaseIndent();
+        if (!mApfCapabilities.hasDataAccess()) {
+            pw.println("APF counters not supported");
+        } else if (mDataSnapshot == null) {
+            pw.println("No last snapshot.");
+        } else {
+            try {
+                Counter[] counters = Counter.class.getEnumConstants();
+                for (Counter c : Arrays.asList(counters).subList(1, counters.length)) {
+                    long value = counterValue(mDataSnapshot, c);
+                    // Only print non-zero counters
+                    if (value != 0) {
+                        pw.println(c.toString() + ": " + value);
+                    }
+                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                pw.println("Uh-oh: " + e);
+            }
+            if (VDBG) {
+                pw.println("Raw data dump: ");
+                pw.println(HexDump.dumpHexString(mDataSnapshot));
+            }
+        }
+        pw.decreaseIndent();
     }
 
     // TODO: move to android.net.NetworkUtils
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index 99b2fc6..87a1b5e 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -378,8 +378,7 @@
     }
 
     /**
-     * Returns true if the specified {@code version} is supported by the ApfGenerator, otherwise
-     * false.
+     * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false.
      */
     public static boolean supportsVersion(int version) {
         return version >= MIN_APF_VERSION;
@@ -753,7 +752,7 @@
 
     /**
      * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
-     * packet at, an offset specified by {@code register}, match {@code bytes}.
+     * packet at an offset specified by {@code register} match {@code bytes}.
      */
     public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
             throws IllegalInstructionException {
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 9fdb31e..d6999dd 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -624,7 +624,14 @@
     private ApfFilter mApfFilter;
     private boolean mMulticastFiltering;
     private long mStartTimeMillis;
-    private byte[] mApfDataSnapshot;
+
+    /**
+     * Reading the snapshot is an asynchronous operation initiated by invoking
+     * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
+     * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
+     * signals when a new snapshot is ready.
+     */
+    private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
 
     public static class Dependencies {
         public INetworkManagementService getNMS() {
@@ -881,13 +888,21 @@
         final ProvisioningConfiguration provisioningConfig = mConfiguration;
         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
                 ? provisioningConfig.mApfCapabilities : null;
-        final byte[] apfDataSnapshot = mApfDataSnapshot;
 
         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         pw.println(mTag + " APF dump:");
         pw.increaseIndent();
         if (apfFilter != null) {
+            if (apfCapabilities.hasDataAccess()) {
+                // Request a new snapshot, then wait for it.
+                mApfDataSnapshotComplete.close();
+                mCallback.startReadPacketFilter();
+                if (!mApfDataSnapshotComplete.block(1000)) {
+                    pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
+                }
+            }
             apfFilter.dump(pw);
+
         } else {
             pw.print("No active ApfFilter; ");
             if (provisioningConfig == null) {
@@ -899,15 +914,6 @@
             }
         }
         pw.decreaseIndent();
-        pw.println(mTag + " latest APF data snapshot: ");
-        pw.increaseIndent();
-        if (apfDataSnapshot != null) {
-            pw.println(HexDump.dumpHexString(apfDataSnapshot));
-        } else {
-            pw.println("No last snapshot.");
-        }
-        pw.decreaseIndent();
-
         pw.println();
         pw.println(mTag + " current ProvisioningConfiguration:");
         pw.increaseIndent();
@@ -1704,7 +1710,10 @@
                 }
 
                 case EVENT_READ_PACKET_FILTER_COMPLETE: {
-                    mApfDataSnapshot = (byte[]) msg.obj;
+                    if (mApfFilter != null) {
+                        mApfFilter.setDataSnapshot((byte[]) msg.obj);
+                    }
+                    mApfDataSnapshotComplete.open();
                     break;
                 }
 
diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
new file mode 100644
index 0000000..8d3de3c
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
@@ -0,0 +1,89 @@
+package com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+/**
+ * Unit tests for {@link GnssNavigationMessageProvider}.
+ */
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        sdk = 27
+)
+@SystemLoaderPackages({"com.android.server.location"})
+@Presubmit
+public class GnssNavigationMessageProviderTest {
+    @Mock
+    private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative mMockNative;
+    private GnssNavigationMessageProvider mTestProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockNative.startNavigationMessageCollection()).thenReturn(true);
+        when(mMockNative.stopNavigationMessageCollection()).thenReturn(true);
+
+        mTestProvider = new GnssNavigationMessageProvider(new Handler(Looper.myLooper()),
+                mMockNative) {
+            @Override
+            public boolean isGpsEnabled() {
+                return true;
+            }
+        };
+    }
+
+    @Test
+    public void register_nativeStarted() {
+        mTestProvider.registerWithService();
+        verify(mMockNative).startNavigationMessageCollection();
+    }
+
+    @Test
+    public void unregister_nativeStopped() {
+        mTestProvider.registerWithService();
+        mTestProvider.unregisterFromService();
+        verify(mMockNative).stopNavigationMessageCollection();
+    }
+
+    @Test
+    public void isSupported_nativeIsSupported() {
+        when(mMockNative.isNavigationMessageSupported()).thenReturn(true);
+        assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
+
+        when(mMockNative.isNavigationMessageSupported()).thenReturn(false);
+        assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
+    }
+
+    @Test
+    public void register_resume_started() {
+        mTestProvider.registerWithService();
+        mTestProvider.resumeIfStarted();
+        verify(mMockNative, times(2)).startNavigationMessageCollection();
+    }
+
+    @Test
+    public void unregister_resume_notStarted() {
+        mTestProvider.registerWithService();
+        mTestProvider.unregisterFromService();
+        mTestProvider.resumeIfStarted();
+        verify(mMockNative, times(1)).startNavigationMessageCollection();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
index 43701e9..c004074 100644
--- a/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
@@ -898,6 +898,10 @@
 
     @Test
     public void accessibility_colorInversion_transformActivated() {
+        if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+            return;
+        }
+
         setAccessibilityColorInversion(true);
         setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
 
@@ -909,6 +913,10 @@
 
     @Test
     public void accessibility_colorCorrection_transformActivated() {
+        if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+            return;
+        }
+
         setAccessibilityColorCorrection(true);
         setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
 
@@ -920,6 +928,10 @@
 
     @Test
     public void accessibility_all_transformActivated() {
+        if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+            return;
+        }
+
         setAccessibilityColorCorrection(true);
         setAccessibilityColorInversion(true);
         setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
@@ -932,6 +944,10 @@
 
     @Test
     public void accessibility_none_transformActivated() {
+        if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+            return;
+        }
+
         setAccessibilityColorCorrection(false);
         setAccessibilityColorInversion(false);
         setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 9daea1a..1415ada 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -19,51 +19,44 @@
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor
+        .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.ArgumentMatchers.any;
-
-import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.WaitResult;
-import android.content.ComponentName;
-import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseIntArray;
 
-import org.junit.runner.RunWith;
 import org.junit.Before;
 import org.junit.Test;
-
+import org.junit.runner.RunWith;
 import org.mockito.invocation.InvocationOnMock;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 
 /**
  * Tests for the {@link ActivityStackSupervisor} class.
@@ -378,4 +371,28 @@
         assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
                 true /* considerKeyguardState */));
     }
+
+    /**
+     * Verify that split-screen primary stack will be chosen if activity is launched that targets
+     * split-screen secondary, but a matching existing instance is found on top of split-screen
+     * primary stack.
+     */
+    @Test
+    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() throws Exception {
+        // Create primary split-screen stack with a task and an activity.
+        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+        // Find a launch stack for the top activity in split-screen primary, while requesting
+        // split-screen secondary.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
+
+        // Assert that the primary stack is returned.
+        assertEquals(primaryStack, result);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 592f7b1..b73ac89 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
@@ -303,6 +304,8 @@
 
     @Test
     public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
+        // Test with undefined activity type since the type is not persisted by the task persister
+        // and we want to ensure that a new task will match a restored task
         Configuration config1 = new Configuration();
         config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
@@ -355,6 +358,65 @@
     }
 
     @Test
+    public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        task1.onConfigurationChanged(config1);
+        assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED);
+        mRecentTasks.add(task1);
+        mCallbacksRecorder.clear();
+
+        Configuration config2 = new Configuration();
+        config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        task2.onConfigurationChanged(config2);
+        assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
+        mRecentTasks.add(task2);
+
+        assertTrue(mCallbacksRecorder.added.size() == 1);
+        assertTrue(mCallbacksRecorder.added.contains(task2));
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.size() == 1);
+        assertTrue(mCallbacksRecorder.removed.contains(task1));
+    }
+
+    @Test
+    public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        task1.onConfigurationChanged(config1);
+        assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
+        mRecentTasks.add(task1);
+
+        Configuration config2 = new Configuration();
+        config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .build();
+        task2.onConfigurationChanged(config2);
+        assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED);
+        mRecentTasks.add(task2);
+
+        assertTrue(mCallbacksRecorder.added.size() == 2);
+        assertTrue(mCallbacksRecorder.added.contains(task1));
+        assertTrue(mCallbacksRecorder.added.contains(task2));
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.isEmpty());
+    }
+
+    @Test
     public void testUsersTasks() throws Exception {
         mRecentTasks.setOnlyTestVisibleRange();
 
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
index 64637f4..30665b5 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
@@ -28,12 +28,19 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
 
+import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.graphics.PixelFormat;
 import android.platform.test.annotations.Presubmit;
@@ -194,4 +201,53 @@
                 PhoneWindowManager.updateLightNavigationBarLw(0, opaqueDarkNavBar,
                         opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
     }
+
+    @Test
+    public void testIsDockSideAllowedDockTop() throws Exception {
+        // Docked top is always allowed
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedDockBottom() throws Exception {
+        // Cannot dock bottom
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarMovable() throws Exception {
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_LEFT,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_RIGHT,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                true /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_RIGHT,
+                true /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_LEFT,
+                true /* navigationBarCanMove */));
+    }
+
+    @Test
+    public void testIsDockSideAllowedNavigationBarNotMovable() throws Exception {
+        // Navigation bar is not movable such as tablets
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertFalse(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+        assertTrue(PhoneWindowManager.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, NAV_BAR_BOTTOM,
+                false /* navigationBarCanMove */));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 1367f58..62fe6b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -144,6 +145,11 @@
         void runOnBgThread(Runnable r) {
             r.run();
         }
+
+        @Override
+        void runOnBgThreadLazy(Runnable r, int delayMillis) {
+            r.run();
+        }
     }
 
     @Before
@@ -153,7 +159,7 @@
         mMockBatterySaverController = mock(BatterySaverController.class);
 
         doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
-                .when(mMockBatterySaverController).enableBatterySaver(anyBoolean());
+                .when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
         when(mMockBatterySaverController.isEnabled())
                 .thenAnswer((inv) -> mDevice.batterySaverEnabled);
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 6019958..ff631e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -545,7 +545,7 @@
                 .restart(BOUNDS_SMALLER_FLOATING,
                         false /* expectStartedAndPipModeChangedCallback */)
                 .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
     }
 
     /** !F->!F w/ CANCEL **/
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 1c2d538..013c672 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -552,7 +552,8 @@
     }
 
     @Override
-    public boolean isDockSideAllowed(int dockSide) {
+    public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
+            int displayHeight, int displayRotation) {
         return false;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index 41e446b..513c1ec 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -208,7 +208,8 @@
         final WindowConfiguration winConfig = config.windowConfiguration;
         stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
                 new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
-                false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig);
+                false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
+                windowingMode);
         // Assert that both expected and actual are null or are equal to each other
 
         assertEquals(expectedConfigBounds, winConfig.getAppBounds());
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index d74defc..2e4740b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -72,7 +73,7 @@
             config.windowConfiguration.setBounds(bounds);
             return null;
         }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
-                anyBoolean(), anyBoolean(), anyFloat(), any(), any());
+                anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
 
         return controller;
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index eb1c997..ac23d14 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -134,6 +135,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -528,7 +530,7 @@
                 PKG, new ParceledListSlice(Arrays.asList(channel)));
 
         final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
-        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
@@ -557,7 +559,7 @@
                 mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
 
         final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
-        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
@@ -601,7 +603,7 @@
         mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
@@ -759,7 +761,7 @@
     @Test
     public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
@@ -773,7 +775,7 @@
     @Test
     public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
@@ -901,7 +903,7 @@
     @Test
     public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
@@ -909,14 +911,14 @@
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
-        assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE);
+        assertEquals(0, notifs[0].getNotification().flags & FLAG_FOREGROUND_SERVICE);
     }
 
     @Test
     public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags =
-                Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
+                Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
@@ -937,7 +939,7 @@
                 mTestNotificationChannel, 2, "group", false);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         final NotificationRecord newGroup = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group2", false);
         mService.addNotification(parent);
@@ -960,7 +962,7 @@
                 mTestNotificationChannel, 2, "group", false);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         final NotificationRecord newGroup = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group2", false);
         mService.addNotification(parent);
@@ -984,7 +986,7 @@
                 mTestNotificationChannel, 2, "group", false);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, "group", false);
-        child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         final NotificationRecord newGroup = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group2", false);
         mService.addNotification(parent);
@@ -2257,7 +2259,7 @@
                 NotificationChannel.DEFAULT_CHANNEL_ID)
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
         StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
@@ -2272,7 +2274,7 @@
         nb = new Notification.Builder(mContext)
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
         sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
@@ -2549,6 +2551,27 @@
     }
 
     @Test
+    public void testVisitUris() throws Exception {
+        final Uri audioContents = Uri.parse("content://com.example/audio");
+        final Uri backgroundImage = Uri.parse("content://com.example/background");
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(Notification.EXTRA_AUDIO_CONTENTS_URI, audioContents);
+        extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, backgroundImage.toString());
+
+        Notification n = new Notification.Builder(mContext, "a")
+                .setContentTitle("notification with uris")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addExtras(extras)
+                .build();
+
+        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+        n.visitUris(visitor);
+        verify(visitor, times(1)).accept(eq(audioContents));
+        verify(visitor, times(1)).accept(eq(backgroundImage));
+    }
+
+    @Test
     public void testSetNotificationPolicy_preP_setOldFields() {
         ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class);
         mService.mZenModeHelper = mZenModeHelper;
@@ -2670,6 +2693,26 @@
     }
 
     @Test
+    public void testVisualDifference_foreground() {
+        Notification.Builder nb1 = new Notification.Builder(mContext, "")
+                .setContentTitle("foo");
+        StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb1.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r1 =
+                new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+        Notification.Builder nb2 = new Notification.Builder(mContext, "")
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .setContentTitle("bar");
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+                nb2.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord r2 =
+                new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+        assertFalse(mService.isVisuallyInterruptive(r1, r2));
+    }
+
+    @Test
     public void testVisualDifference_diffTitle() {
         Notification.Builder nb1 = new Notification.Builder(mContext, "")
                 .setContentTitle("foo");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index d846d21..36ec221 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -336,6 +336,90 @@
     }
 
     @Test
+    public void testRemoteViews_layoutChange() {
+        RemoteViews a = mock(RemoteViews.class);
+        when(a.getLayoutId()).thenReturn(234);
+        RemoteViews b = mock(RemoteViews.class);
+        when(b.getLayoutId()).thenReturn(189);
+
+        Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+        Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+    }
+
+    @Test
+    public void testRemoteViews_layoutSame() {
+        RemoteViews a = mock(RemoteViews.class);
+        when(a.getLayoutId()).thenReturn(234);
+        RemoteViews b = mock(RemoteViews.class);
+        when(b.getLayoutId()).thenReturn(234);
+
+        Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+        Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+    }
+
+    @Test
+    public void testRemoteViews_sequenceChange() {
+        RemoteViews a = mock(RemoteViews.class);
+        when(a.getLayoutId()).thenReturn(234);
+        when(a.getSequenceNumber()).thenReturn(1);
+        RemoteViews b = mock(RemoteViews.class);
+        when(b.getLayoutId()).thenReturn(234);
+        when(b.getSequenceNumber()).thenReturn(2);
+
+        Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+        Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+        assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+    }
+
+    @Test
+    public void testRemoteViews_sequenceSame() {
+        RemoteViews a = mock(RemoteViews.class);
+        when(a.getLayoutId()).thenReturn(234);
+        when(a.getSequenceNumber()).thenReturn(1);
+        RemoteViews b = mock(RemoteViews.class);
+        when(b.getLayoutId()).thenReturn(234);
+        when(b.getSequenceNumber()).thenReturn(1);
+
+        Notification.Builder n1 = new Notification.Builder(mContext, "test").setContent(a);
+        Notification.Builder n2 = new Notification.Builder(mContext, "test").setContent(b);
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomBigContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomBigContentView(b);
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+
+        n1 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(a);
+        n2 = new Notification.Builder(mContext, "test").setCustomHeadsUpContentView(b);
+        assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+    }
+
+    @Test
     public void testActionsDifferent_null() {
         Notification n1 = new Notification.Builder(mContext, "test")
                 .build();
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index d49ba3e..43a4e27 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -93,7 +93,7 @@
 
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
-        verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), eq("pkg"));
+        verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), anyString());
     }
 
     @Test
@@ -126,4 +126,4 @@
         verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
     }
 
-}
\ No newline at end of file
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index c914689..0dce738 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -16,6 +16,7 @@
 package com.android.server.usage;
 
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
 import android.app.usage.EventStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
@@ -37,7 +38,7 @@
     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
-    public TimeSparseArray<UsageEvents.Event> events;
+    public EventList events;
 
     // A string cache. This is important as when we're parsing XML files, we don't want to
     // keep hundreds of strings that have the same contents. We will read the string
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index fe3a884..aa832ad 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -22,12 +22,11 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.app.usage.ConfigurationStats;
-import android.app.usage.TimeSparseArray;
+import android.app.usage.EventList;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
 import android.util.ArrayMap;
-import android.util.Pair;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -193,9 +192,9 @@
         }
 
         if (statsOut.events == null) {
-            statsOut.events = new TimeSparseArray<>();
+            statsOut.events = new EventList();
         }
-        statsOut.events.put(event.mTimeStamp, event);
+        statsOut.events.insert(event);
     }
 
     private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
@@ -411,7 +410,7 @@
         xml.startTag(null, EVENT_LOG_TAG);
         final int eventCount = stats.events != null ? stats.events.size() : 0;
         for (int i = 0; i < eventCount; i++) {
-            writeEvent(xml, stats, stats.events.valueAt(i));
+            writeEvent(xml, stats, stats.events.get(i));
         }
         xml.endTag(null, EVENT_LOG_TAG);
     }
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d9fc066..9cb98f3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -17,15 +17,14 @@
 package com.android.server.usage;
 
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
 import android.app.usage.EventStats;
-import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.res.Configuration;
 import android.os.SystemClock;
 import android.content.Context;
-import android.text.format.DateFormat;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -174,10 +173,10 @@
 
         // Add the event to the daily list.
         if (currentDailyStats.events == null) {
-            currentDailyStats.events = new TimeSparseArray<>();
+            currentDailyStats.events = new EventList();
         }
         if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
-            currentDailyStats.events.put(event.mTimeStamp, event);
+            currentDailyStats.events.insert(event);
         }
 
         boolean incrementAppLaunch = false;
@@ -367,18 +366,14 @@
                             return;
                         }
 
-                        final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
-                        if (startIndex < 0) {
-                            return;
-                        }
-
+                        final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
                         for (int i = startIndex; i < size; i++) {
-                            if (stats.events.keyAt(i) >= endTime) {
+                            if (stats.events.get(i).mTimeStamp >= endTime) {
                                 return;
                             }
 
-                            UsageEvents.Event event = stats.events.valueAt(i);
+                            UsageEvents.Event event = stats.events.get(i);
                             if (obfuscateInstantApps) {
                                 event = event.getObfuscatedIfInstantApp();
                             }
@@ -410,18 +405,14 @@
                         return;
                     }
 
-                    final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
-                    if (startIndex < 0) {
-                        return;
-                    }
-
+                    final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                     final int size = stats.events.size();
                     for (int i = startIndex; i < size; i++) {
-                        if (stats.events.keyAt(i) >= endTime) {
+                        if (stats.events.get(i).mTimeStamp >= endTime) {
                             return;
                         }
 
-                        final UsageEvents.Event event = stats.events.valueAt(i);
+                        final UsageEvents.Event event = stats.events.get(i);
                         if (!packageName.equals(event.mPackage)) {
                             continue;
                         }
@@ -633,18 +624,14 @@
                             return;
                         }
 
-                        final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
-                        if (startIndex < 0) {
-                            return;
-                        }
-
+                        final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
                         final int size = stats.events.size();
                         for (int i = startIndex; i < size; i++) {
-                            if (stats.events.keyAt(i) >= endTime) {
+                            if (stats.events.get(i).mTimeStamp >= endTime) {
                                 return;
                             }
 
-                            UsageEvents.Event event = stats.events.valueAt(i);
+                            UsageEvents.Event event = stats.events.get(i);
                             if (pkg != null && !pkg.equals(event.mPackage)) {
                                 continue;
                             }
@@ -779,10 +766,10 @@
         if (!skipEvents) {
             pw.println("events");
             pw.increaseIndent();
-            final TimeSparseArray<UsageEvents.Event> events = stats.events;
+            final EventList events = stats.events;
             final int eventCount = events != null ? events.size() : 0;
             for (int i = 0; i < eventCount; i++) {
-                final UsageEvents.Event event = events.valueAt(i);
+                final UsageEvents.Event event = events.get(i);
                 if (pkg != null && !pkg.equals(event.mPackage)) {
                     continue;
                 }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index f164bcd..e213d79 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2964,12 +2964,30 @@
          * @hide
          */
         public static final int OWNED_BY_DPC = 0;
+
         /**
          * Possible value for the OWNED_BY field.
          * APN is owned by other sources.
          * @hide
          */
         public static final int OWNED_BY_OTHERS = 1;
+
+        /**
+         * The APN set id. When the user manually selects an APN or the framework sets an APN as
+         * preferred, all APNs with the same set id as the selected APN should be prioritized over
+         * APNs in other sets.
+         * @hide
+         */
+        public static final String APN_SET_ID = "apn_set_id";
+
+        /**
+         * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
+         * user manually selects an APN with no set set, there is no need to prioritize any specific
+         * APN set ids.
+         * @hide
+         */
+        public static final int NO_SET_SET = 0;
+
     }
 
     /**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 7db83f6..88bed4e 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -205,6 +206,16 @@
     public static final int LISTEN_VOLTE_STATE                              = 0x00004000;
 
     /**
+     * Listen for OEM hook raw event
+     *
+     * @see #onOemHookRawEvent
+     * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+     */
+    @Deprecated
+    public static final int LISTEN_OEM_HOOK_RAW_EVENT                       = 0x00008000;
+
+    /**
      * Listen for carrier network changes indicated by a carrier app.
      *
      * @see #onCarrierNetworkRequest
@@ -368,6 +379,9 @@
                     case LISTEN_USER_MOBILE_DATA_STATE:
                         PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
                         break;
+                    case LISTEN_OEM_HOOK_RAW_EVENT:
+                        PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
+                        break;
                     case LISTEN_CARRIER_NETWORK_CHANGE:
                         PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
                         break;
@@ -583,7 +597,18 @@
      * @param configs List of the current {@link PhysicalChannelConfig}s
      * @hide
      */
-    public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+    public void onPhysicalChannelConfigurationChanged(
+            @NonNull List<PhysicalChannelConfig> configs) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when OEM hook raw event is received. Requires
+     * the READ_PRIVILEGED_PHONE_STATE permission.
+     * @param rawData is the byte array of the OEM hook raw data.
+     * @hide
+     */
+    public void onOemHookRawEvent(byte[] rawData) {
         // default implementation empty
     }
 
@@ -703,6 +728,10 @@
             send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
         }
 
+        public void onOemHookRawEvent(byte[] rawData) {
+            send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
+        }
+
         public void onCarrierNetworkChange(boolean active) {
             send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index eae010f..43ec716 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2786,6 +2786,22 @@
     }
 
     /**
+     * Test method to reload the UICC profile.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void refreshUiccProfile() {
+        try {
+            ITelephony telephony = getITelephony();
+            telephony.refreshUiccProfile(mSubId);
+        } catch (RemoteException ex) {
+            Rlog.w(TAG, "RemoteException", ex);
+        }
+    }
+
+    /**
      * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. For
      * example, passing the physicalSlots array [1, 0] means mapping the first item 1, which is
      * physical slot index 1, to the logical slot 0; and mapping the second item 0, which is
@@ -6506,6 +6522,29 @@
         return retVal;
     }
 
+    /**
+     * Returns the result and response from RIL for oem request
+     *
+     * @param oemReq the data is sent to ril.
+     * @param oemResp the respose data from RIL.
+     * @return negative value request was not handled or get error
+     *         0 request was handled succesfully, but no response data
+     *         positive value success, data length of response
+     * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+     */
+    @Deprecated
+    public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return -1;
+    }
+
     /** @hide */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 1cfe8c2..0d315e5 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -47,6 +47,7 @@
     void onVoLteServiceStateChanged(in VoLteServiceState lteState);
     void onVoiceActivationStateChanged(int activationState);
     void onDataActivationStateChanged(int activationState);
+    void onOemHookRawEvent(in byte[] rawData);
     void onCarrierNetworkChange(in boolean active);
     void onUserMobileDataStateChanged(in boolean enabled);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bb771bb..f02f27d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1065,6 +1065,17 @@
             in List<String> cdmaNonRoamingList);
 
     /**
+     * Returns the result and response from RIL for oem request
+     *
+     * @param oemReq the data is sent to ril.
+     * @param oemResp the respose data from RIL.
+     * @return negative value request was not handled or get error
+     *         0 request was handled succesfully, but no response data
+     *         positive value success, data length of response
+     */
+    int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
+
+    /**
      * Check if any mobile Radios need to be shutdown.
      *
      * @return true is any mobile radio needs to be shutdown
@@ -1503,4 +1514,10 @@
      * A test API to return installed carrier id list version.
      */
     int getCarrierIdListVersion(int subId);
+
+    /**
+     * A test API to reload the UICC profile.
+     * @hide
+     */
+    void refreshUiccProfile(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 06dc13e..0127db9 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -70,6 +70,7 @@
     void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
     void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
             int activationState, int activationType);
+    void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
     void notifySubscriptionInfoChanged();
     void notifyCarrierNetworkChange(in boolean active);
     void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 6f213e1..34b46c5 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -117,8 +117,7 @@
 
 bool Reference::Flatten(android::Res_value* out_value) const {
   const ResourceId resid = id.value_or_default(ResourceId(0));
-  const bool dynamic = resid.is_valid_dynamic() && resid.package_id() != kFrameworkPackageId &&
-                       resid.package_id() < kAppPackageId;
+  const bool dynamic = resid.is_valid_dynamic() && is_dynamic;
 
   if (reference_type == Reference::Type::kResource) {
     if (dynamic) {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 6371c4c..168ad61 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -157,6 +157,7 @@
   Maybe<ResourceId> id;
   Reference::Type reference_type;
   bool private_reference = false;
+  bool is_dynamic = false;
 
   Reference();
   explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index ba11766..75eba9e 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -131,6 +131,12 @@
   // Stable ID options.
   std::unordered_map<ResourceName, ResourceId> stable_id_map;
   Maybe<std::string> resource_id_map_path;
+
+  // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
+  // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
+  // In order to work around this limitation, we allow the use of traditionally reserved
+  // resource IDs [those between 0x02 and 0x7E].
+  bool allow_reserved_package_id = false;
 };
 
 class LinkContext : public IAaptContext {
@@ -910,9 +916,7 @@
     // Capture the shared libraries so that the final resource table can be properly flattened
     // with support for shared libraries.
     for (auto& entry : asset_source->GetAssignedPackageIds()) {
-      if (entry.first > kFrameworkPackageId && entry.first < kAppPackageId) {
-        final_table_.included_packages_[entry.first] = entry.second;
-      } else if (entry.first == kAppPackageId) {
+      if (entry.first == kAppPackageId) {
         // Capture the included base feature package.
         included_feature_base_ = entry.second;
       } else if (entry.first == kFrameworkPackageId) {
@@ -926,6 +930,8 @@
           // android:versionCode from the framework AndroidManifest.xml.
           ExtractCompileSdkVersions(asset_source->GetAssetManager());
         }
+      } else if (asset_source->IsPackageDynamic(entry.first)) {
+        final_table_.included_packages_[entry.first] = entry.second;
       }
     }
 
@@ -1610,7 +1616,15 @@
     // If required, the package name is modifed before flattening, and then modified back
     // to its original name.
     ResourceTablePackage* package_to_rewrite = nullptr;
-    if (context_->GetPackageId() > kAppPackageId &&
+    // Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80
+    // or higher] as invalid. In order to work around this limitation, we allow the use
+    // of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the
+    // definition of what a valid "split" package ID is to account for this.
+    const bool isSplitPackage = (options_.allow_reserved_package_id &&
+          context_->GetPackageId() != kAppPackageId &&
+          context_->GetPackageId() != kFrameworkPackageId)
+        || (!options_.allow_reserved_package_id && context_->GetPackageId() > kAppPackageId);
+    if (isSplitPackage &&
         included_feature_base_ == make_value(context_->GetCompilationPackage())) {
       // The base APK is included, and this is a feature split. If the base package is
       // the same as this package, then we are building an old style Android Instant Apps feature
@@ -2165,6 +2179,10 @@
                         "Generates a text file containing the resource symbols of the R class in\n"
                         "the specified folder.",
                         &options.generate_text_symbols_path)
+          .OptionalSwitch("--allow-reserved-package-id",
+                          "Allows the use of a reserved package ID. This should on be used for\n"
+                          "packages with a pre-O min-sdk\n",
+                          &options.allow_reserved_package_id)
           .OptionalSwitch("--auto-add-overlay",
                           "Allows the addition of new resources in overlays without\n"
                           "<add-resource> tags.",
@@ -2275,7 +2293,9 @@
     }
 
     const uint32_t package_id_int = maybe_package_id_int.value();
-    if (package_id_int < kAppPackageId || package_id_int > std::numeric_limits<uint8_t>::max()) {
+    if (package_id_int > std::numeric_limits<uint8_t>::max()
+        || package_id_int == kFrameworkPackageId
+        || (!options.allow_reserved_package_id && package_id_int < kAppPackageId)) {
       context.GetDiagnostics()->Error(
           DiagMessage() << StringPrintf(
               "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 13d2a04..28e71cc 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -348,6 +348,7 @@
     // against libraries without assigned IDs.
     // Ex: Linking against own resources when building a static library.
     reference->id = s->id;
+    reference->is_dynamic = s->is_dynamic;
     return true;
   }
 
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index f6f0a50..ef2e448 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -256,6 +256,10 @@
   return package_map;
 }
 
+bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
+  return assets_.getResources(false).isPackageDynamic(packageId);
+}
+
 static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
     const android::ResTable& table, ResourceId id) {
   // Try as a bag.
@@ -359,6 +363,7 @@
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = res_id;
+    s->is_dynamic = table.isResourceDynamic(res_id.id);
   }
 
   if (s) {
@@ -383,7 +388,6 @@
     // Exit early and avoid the error logs from AssetManager.
     return {};
   }
-
   const android::ResTable& table = assets_.getResources(false);
   Maybe<ResourceName> maybe_name = GetResourceName(table, id);
   if (!maybe_name) {
@@ -399,6 +403,7 @@
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = id;
+    s->is_dynamic = table.isResourceDynamic(id.id);
   }
 
   if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index c80e627..c798cbb 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -68,6 +68,7 @@
     Maybe<ResourceId> id;
     std::shared_ptr<Attribute> attribute;
     bool is_public = false;
+    bool is_dynamic = false;
   };
 
   SymbolTable(NameMangler* mangler);
@@ -205,6 +206,7 @@
 
   bool AddAssetPath(const android::StringPiece& path);
   std::map<size_t, std::string> GetAssignedPackageIds() const;
+  bool IsPackageDynamic(uint32_t packageId) const;
 
   std::unique_ptr<SymbolTable::Symbol> FindByName(
       const ResourceName& name) override;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index b333126..a7fffca 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -85,7 +85,7 @@
 
     boolean disableNetwork(int netId, String packageName);
 
-    void startScan(String packageName);
+    boolean startScan(String packageName);
 
     List<ScanResult> getScanResults(String callingPackage);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9c6c8a9..a19965d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1635,8 +1635,7 @@
     public boolean startScan(WorkSource workSource) {
         try {
             String packageName = mContext.getOpPackageName();
-            mService.startScan(packageName);
-            return true;
+            return mService.startScan(packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f3ffcad..20e49cd 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1070,4 +1070,16 @@
             fail("setWifiApConfiguration should rethrow Exceptions from WifiService");
         } catch (SecurityException e) { }
     }
+
+    /**
+     * Check the call to startScan calls WifiService.
+     */
+    @Test
+    public void testStartScan() throws Exception {
+        when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(true);
+        assertTrue(mWifiManager.startScan());
+
+        when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(false);
+        assertFalse(mWifiManager.startScan());
+    }
 }