Merge changes Id3285b63,Ie8e9398c

* changes:
  Move all non-GL HW Bitmap work off RT
  Remove RenderThread from EglManager
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/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index a1219ea..2c04e30 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;
@@ -1086,8 +1085,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
@@ -2374,6 +2375,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 +2966,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 +3178,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 +3232,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 +3937,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..f18b92a 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) {
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/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/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/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/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/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/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/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/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/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index eadefc9..6bf2e0e 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) {
@@ -1901,9 +1906,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 +2016,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/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f569132..f090aca 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);
+                }
             }
         }
     }
@@ -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/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/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/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/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 d7a1ecc..5298e5b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2964,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" />
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/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/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/protoutil/Android.bp b/libs/protoutil/Android.bp
index 14b2e41..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,15 +36,24 @@
     ],
 }
 
+cc_library {
+    name: "libprotoutil",
+
+    defaults: ["libprotoutil_defaults"],
+
+    export_include_dirs: ["include"],
+}
+
 cc_test {
     name: "libprotoutil_test",
 
+    defaults: ["libprotoutil_defaults"],
+
+    local_include_dirs: ["include"],
+
     srcs: ["tests/*"],
 
     shared_libs: [
-        "libbase",
-        "libcutils",
-        "libprotoutil",
         "libprotobuf-cpp-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/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
index e8e1557..27ee13d 100644
--- a/libs/protoutil/tests/ProtoOutputStream_test.cpp
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -132,17 +132,31 @@
     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_EQ(proto.size(), 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);
+    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) + 2);
+    proto.writeRawByte((ComplexProto::Log::kDataFieldNumber << FIELD_ID_SHIFT) + WIRE_TYPE_LENGTH_DELIMITED);
     proto.writeRawByte(6);
     proto.writeRawByte('b');
     proto.writeRawByte('a');
@@ -168,3 +182,47 @@
     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/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/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/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/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/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/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/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/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/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/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..438d2f5 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>
 
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/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/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/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 bba271b..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);
@@ -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);
+            };
         }
     };
 
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/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/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/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/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/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/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/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/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/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/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/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 4d8718f..72d1722 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8433,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);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a35b193..b3003ac 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6094,7 +6094,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);
         }
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/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/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/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/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..37c646b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -134,6 +134,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -2549,6 +2550,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;
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/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());
+    }
 }