[automerger skipped] Merge "Pass filtering to drawLattice" into sc-dev am: 417f6b1677 am: 07a49e9c89 -s ours

am skip reason: Change-Id I14541a21abb885c5291ca839d72ed6434c9accd1 with SHA-1 38c1e55f4b is in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13418275

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I7c8b4796c475aa8d024076c633e35e8dc24f309e
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
new file mode 100644
index 0000000..3d50d14
--- /dev/null
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
@@ -0,0 +1,21 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+/** {@hide} */
+interface IMediaCommunicationService {
+}
+
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 60dea07..5773e4d 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -38,6 +38,7 @@
     static_libs: [
         "exoplayer2-extractor",
         "mediatranscoding_aidl_interface-java",
+        "modules-utils-build",
     ],
     jarjar_rules: "jarjar_rules.txt",
 
@@ -52,6 +53,7 @@
     visibility: [
         "//frameworks/av/apex:__subpackages__",
         "//frameworks/base", // For framework-all
+        "//frameworks/base/apex/media/service",
     ],
 }
 
@@ -80,6 +82,7 @@
         "java/android/media/Session2CommandGroup.java",
         "java/android/media/Session2Link.java",
         "java/android/media/Session2Token.java",
+        "java/android/media/MediaCommunicationManager.java",
     ],
     path: "java",
 }
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 2543a9c..8b9990f 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -28,6 +28,9 @@
     ctor public ApplicationMediaCapabilities.FormatNotFoundException(@NonNull String);
   }
 
+  public class MediaCommunicationManager {
+  }
+
   public class MediaController2 implements java.lang.AutoCloseable {
     method public void cancelSessionCommand(@NonNull Object);
     method public void close();
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
index d89d9d3..eb71fdd 100644
--- a/apex/media/framework/jarjar_rules.txt
+++ b/apex/media/framework/jarjar_rules.txt
@@ -1 +1,2 @@
+rule com.android.modules.utils.** android.media.internal.utils.@1
 rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
new file mode 100644
index 0000000..b8065ef
--- /dev/null
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+import com.android.modules.utils.build.SdkLevel;
+
+/**
+ * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s}
+ * that applications have published to express their ongoing media playback state.
+ */
+// TODO: Add notifySession2Created() and sendMessage().
+@SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
+public class MediaCommunicationManager {
+    private static final String TAG = "MediaCommunicationManager";
+
+    private final Context mContext;
+    private final IMediaCommunicationService mService;
+
+    /**
+     * @hide
+     */
+    public MediaCommunicationManager(@NonNull Context context) {
+        if (!SdkLevel.isAtLeastS()) {
+            throw new UnsupportedOperationException("Android version must be S or greater.");
+        }
+        mContext = context;
+        mService = IMediaCommunicationService.Stub.asInterface(
+                MediaFrameworkInitializer.getMediaServiceManager()
+                .getMediaCommunicationServiceRegisterer()
+                .get());
+    }
+}
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index 813ad7b..9332835 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -19,10 +19,11 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.SystemApi.Client;
-import android.media.MediaTranscodeManager;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
 
+import com.android.modules.utils.build.SdkLevel;
+
 /**
  * Class for performing registration for all media services on com.android.media apex.
  *
@@ -74,5 +75,12 @@
                 MediaTranscodeManager.class,
                 context -> new MediaTranscodeManager(context)
         );
+        if (SdkLevel.isAtLeastS()) {
+            SystemServiceRegistry.registerContextAwareService(
+                    Context.MEDIA_COMMUNICATION_SERVICE,
+                    MediaCommunicationManager.class,
+                    context -> new MediaCommunicationManager(context)
+            );
+        }
     }
 }
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
new file mode 100644
index 0000000..5b24cfa
--- /dev/null
+++ b/apex/media/service/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 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.
+filegroup {
+    name: "service-media-s-sources",
+    srcs: [
+      "java/**/*.java",
+    ],
+    path: "java",
+    visibility: ["//frameworks/base/services"], // TODO(b/177640454): Should be private.
+}
+
+java_sdk_library {
+    name: "service-media-s",
+    permitted_packages: [
+        "com.android.server.media",
+    ],
+    defaults: ["framework-system-server-module-defaults"],
+    srcs: [
+        ":service-media-s-sources",
+    ],
+    libs: [
+        "updatable-media",
+    ],
+    sdk_version: "system_server_current",
+    min_sdk_version: "29", // TODO: We may need to bump this at some point.
+    apex_available: [
+        "com.android.media",
+    ],
+}
+
diff --git a/apex/media/service/api/current.txt b/apex/media/service/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/api/removed.txt b/apex/media/service/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-current.txt b/apex/media/service/api/system-server-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/system-server-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-removed.txt b/apex/media/service/api/system-server-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/service/api/system-server-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
new file mode 100644
index 0000000..0468fdf
--- /dev/null
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.media;
+
+import android.content.Context;
+import android.media.IMediaCommunicationService;
+
+import com.android.server.SystemService;
+
+/**
+ * A system service that managers {@link android.media.MediaSession2} creations
+ * and their ongoing media playback state.
+ * @hide
+ */
+public class MediaCommunicationService extends SystemService {
+
+    public MediaCommunicationService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub());
+    }
+
+    private class Stub extends IMediaCommunicationService.Stub {
+    }
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 0b81c90..c454666 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10373,6 +10373,7 @@
     field public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
     field public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     field public static final String LOCATION_SERVICE = "location";
+    field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
     field public static final String MEDIA_METRICS_SERVICE = "media_metrics";
     field public static final String MEDIA_PROJECTION_SERVICE = "media_projection";
     field public static final String MEDIA_ROUTER_SERVICE = "media_router";
@@ -15770,6 +15771,7 @@
     method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
     method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
     method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+    method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
   }
 
   public final class RenderNode {
@@ -16798,6 +16800,18 @@
 
 package android.hardware {
 
+  public abstract class Battery {
+    ctor public Battery();
+    method @FloatRange(from=-1.0F, to=1.0f) public abstract float getCapacity();
+    method public abstract int getStatus();
+    method public abstract boolean hasBattery();
+    field public static final int STATUS_CHARGING = 2; // 0x2
+    field public static final int STATUS_DISCHARGING = 3; // 0x3
+    field public static final int STATUS_FULL = 5; // 0x5
+    field public static final int STATUS_NOT_CHARGING = 4; // 0x4
+    field public static final int STATUS_UNKNOWN = 1; // 0x1
+  }
+
   @Deprecated public class Camera {
     method @Deprecated public final void addCallbackBuffer(byte[]);
     method @Deprecated public final void autoFocus(android.hardware.Camera.AutoFocusCallback);
@@ -18498,6 +18512,7 @@
   public final class InputManager {
     method public android.view.InputDevice getInputDevice(int);
     method public int[] getInputDeviceIds();
+    method public float getMaximumObscuringOpacityForTouch();
     method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
     method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener);
     method @Nullable public android.view.VerifiedInputEvent verifyInputEvent(@NonNull android.view.InputEvent);
@@ -38576,6 +38591,7 @@
 
   public class SpeechRecognizer {
     method public void cancel();
+    method @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
     method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
     method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
     method public void destroy();
@@ -39713,7 +39729,6 @@
     field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
     field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
     field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
-    field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
     field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
     field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
     field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION";
@@ -39722,6 +39737,7 @@
     field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
     field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
+    field public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
     field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY";
     field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
     field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -46362,6 +46378,7 @@
 
   public final class InputDevice implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public android.hardware.Battery getBattery();
     method public int getControllerNumber();
     method public String getDescriptor();
     method public static android.view.InputDevice getDevice(int);
@@ -46416,6 +46433,7 @@
     field public static final int SOURCE_MOUSE = 8194; // 0x2002
     field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004
     field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_SENSOR = 67108864; // 0x4000000
     field public static final int SOURCE_STYLUS = 16386; // 0x4002
     field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
     field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 0915d07..4d2040b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -103,6 +103,7 @@
   }
 
   public class MediaServiceManager {
+    method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaCommunicationServiceRegisterer();
     method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaSessionServiceRegisterer();
     method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaTranscodingServiceRegisterer();
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2a2b799..e8909c8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5606,12 +5606,12 @@
   public class Filter implements java.lang.AutoCloseable {
     method public void close();
     method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
-    method public int configureMonitorEvent(int);
     method public int flush();
     method public int getId();
     method public long getId64Bit();
     method public int read(@NonNull byte[], long, long);
     method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
+    method public int setMonitorEventMask(int);
     method public int start();
     method public int stop();
     field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2
@@ -9829,6 +9829,7 @@
     method public void onNotificationDirectReplied(@NonNull String);
     method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification);
     method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
+    method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap);
     method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
     method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
     method public void onNotificationVisibilityChanged(@NonNull String, boolean);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c237d7d..f4079a1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -400,10 +400,10 @@
     field public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
     field public static final int CODE_NONSYSTEM_USER_EXISTS = 5; // 0x5
     field public static final int CODE_NOT_SYSTEM_USER = 7; // 0x7
-    field public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
+    field @Deprecated public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12; // 0xc
     field public static final int CODE_OK = 0; // 0x0
     field public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
-    field public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
+    field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
     field public static final int CODE_SYSTEM_USER = 10; // 0xa
     field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
     field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
@@ -931,9 +931,8 @@
 
   public final class InputManager {
     method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
-    method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(float);
     field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
   }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ada703b..e84d4a5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -453,59 +453,6 @@
             "android.app.action.PROVISION_FINANCED_DEVICE";
 
     /**
-     * Activity action: Starts the provisioning flow which sets up a managed device.
-     * Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
-     *
-     * <p>NOTE: This is only supported on split system user devices, and puts the device into a
-     * management state that is distinct from that reached by
-     * {@link #ACTION_PROVISION_MANAGED_DEVICE} - specifically the device owner runs on the system
-     * user, and only has control over device-wide policies, not individual users and their data.
-     * The primary benefit is that multiple non-system users are supported when provisioning using
-     * this form of device management.
-     *
-     * <p>During device owner provisioning a device admin app is set as the owner of the device.
-     * A device owner has full control over the device. The device owner can not be modified by the
-     * user.
-     *
-     * <p>A typical use case would be a device that is owned by a company, but used by either an
-     * employee or client.
-     *
-     * <p>An intent with this action can be sent only on an unprovisioned device.
-     * It is possible to check if provisioning is allowed or not by querying the method
-     * {@link #isProvisioningAllowed(String)}.
-     *
-     * <p>The intent contains the following extras:
-     * <ul>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
-     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
-     * </ul>
-     *
-     * <p>When device owner provisioning has completed, an intent of the type
-     * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcast to the
-     * device owner.
-     *
-     * <p>From version {@link android.os.Build.VERSION_CODES#O}, when device owner provisioning has
-     * completed, along with the above broadcast, activity intent
-     * {@link #ACTION_PROVISIONING_SUCCESSFUL} will also be sent to the device owner.
-     *
-     * <p>If provisioning fails, the device is factory reset.
-     *
-     * <p>A result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part
-     * of the provisioning flow was successful, although this doesn't guarantee the full flow will
-     * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
-     * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
-        = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
-
-    /**
      * Activity action: Finalizes management provisioning, should be used after user-setup
      * has been completed and {@link #getUserProvisioningState()} returns one of:
      * <ul>
@@ -1990,8 +1937,8 @@
      * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
-     * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when provisioning is allowed.
+     * {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}
+     * when provisioning is allowed.
      *
      * @hide
      */
@@ -2001,9 +1948,8 @@
     /**
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the device already has a device
-     * owner.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the device already has a
+     * device owner.
      *
      * @hide
      */
@@ -2013,9 +1959,8 @@
     /**
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user has a profile owner and for
-     * {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user has a profile owner
+     *  and for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
      *
      * @hide
      */
@@ -2025,8 +1970,7 @@
     /**
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} when the user isn't running.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user isn't running.
      *
      * @hide
      */
@@ -2036,9 +1980,8 @@
     /**
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the device has already been setup and
-     * for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the device has already been
+     * setup and for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
      *
      * @hide
      */
@@ -2064,8 +2007,7 @@
     /**
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} if the user is not a system user.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the user is not a system user.
      *
      * @hide
      */
@@ -2075,9 +2017,8 @@
     /**
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} and {@link #ACTION_PROVISION_MANAGED_USER}
-     * when the device is a watch and is already paired.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
+     * {@link #ACTION_PROVISION_MANAGED_USER} when the device is a watch and is already paired.
      *
      * @hide
      */
@@ -2121,14 +2062,11 @@
 
     /**
      * TODO (b/137101239): clean up split system user codes
-     * Result code for {@link #checkProvisioningPreCondition}.
-     *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices not running with split system
-     * user.
      *
      * @hide
-     */
+     * @deprecated not used anymore but can't be removed since it's a @TestApi.
+     **/
+    @Deprecated
     @TestApi
     public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
 
@@ -2136,8 +2074,7 @@
      * Result code for {@link #checkProvisioningPreCondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
-     * {@link #ACTION_PROVISION_MANAGED_PROFILE}, {@link #ACTION_PROVISION_MANAGED_USER} and
-     * {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE} on devices which do not support device
+     * {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support device
      * admins.
      *
      * @hide
@@ -2149,11 +2086,10 @@
      * TODO (b/137101239): clean up split system user codes
      * Result code for {@link #checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device the user is a
-     * system user on a split system user device.
-     *
      * @hide
+     * @deprecated not used anymore but can't be removed since it's a @TestApi.
      */
+    @Deprecated
     @TestApi
     public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
 
@@ -7147,17 +7083,9 @@
     }
 
     /**
+     * TODO (b/137101239): remove this method in follow-up CL
+     * since it's only used for split system user.
      * Called by a device owner to set whether all users created on the device should be ephemeral.
-     * <p>
-     * The system user is exempt from this policy - it is never ephemeral.
-     * <p>
-     * The calling device admin must be the device owner. If it is not, a security exception will be
-     * thrown.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param forceEphemeralUsers If true, all the existing users will be deleted and all
-     *            subsequently created users will be ephemeral.
-     * @throws SecurityException if {@code admin} is not a device owner.
      * @hide
      */
     public void setForceEphemeralUsers(
@@ -7173,6 +7101,8 @@
     }
 
     /**
+     * TODO (b/137101239): remove this method in follow-up CL
+     * since it's only used for split system user.
      * @return true if all users are created ephemeral.
      * @throws SecurityException if {@code admin} is not a device owner.
      * @hide
@@ -10731,9 +10661,7 @@
      * profile or user, setting the given package as owner.
      *
      * @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
-     *        {@link #ACTION_PROVISION_MANAGED_PROFILE},
-     *        {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE},
-     *        {@link #ACTION_PROVISION_MANAGED_USER}
+     *        {@link #ACTION_PROVISION_MANAGED_PROFILE}
      * @param packageName The package of the component that would be set as device, user, or profile
      *        owner.
      * @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 7eda50e..ea7e5ea 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3198,6 +3198,61 @@
     }
 
     /**
+     * Register a callback to receive events whenever the bluetooth stack goes down and back up,
+     * e.g. in the event the bluetooth is turned off/on via settings.
+     *
+     * If the bluetooth stack is currently up, there will not be an initial callback call.
+     * You can use the return value as an indication of this being the case.
+     *
+     * Callbacks will be delivered on a binder thread.
+     *
+     * @return whether bluetooth is already up currently
+     *
+     * @hide
+     */
+    public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+        return getBluetoothService(callback.mRemote) != null;
+    }
+
+    /**
+     * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
+     *
+     * @hide
+     */
+    public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+        removeServiceStateCallback(callback.mRemote);
+    }
+
+    /**
+     * A callback for {@link #registerServiceLifecycleCallback}
+     *
+     * @hide
+     */
+    public abstract static class ServiceLifecycleCallback {
+
+        /** Called when the bluetooth stack is up */
+        public abstract void onBluetoothServiceUp();
+
+        /** Called when the bluetooth stack is down */
+        public abstract void onBluetoothServiceDown();
+
+        IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
+            @Override
+            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                ServiceLifecycleCallback.this.onBluetoothServiceUp();
+            }
+
+            @Override
+            public void onBluetoothServiceDown() {
+                ServiceLifecycleCallback.this.onBluetoothServiceDown();
+            }
+
+            @Override
+            public void onBrEdrDown() {}
+        };
+    }
+
+    /**
      * Starts a scan for Bluetooth LE devices.
      *
      * <p>Results of the scan are reported using the
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 17bf11b..960a087 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -38,7 +38,7 @@
     private final @NonNull String mDeviceMacAddress;
     private final @NonNull String mPackageName;
     private final @Nullable String mDeviceProfile;
-    private final boolean mKeepProfilePrivilegesWhenDeviceAway;
+    private final boolean mNotifyOnDeviceNearby;
 
     /** @hide */
     public int getUserId() {
@@ -47,7 +47,7 @@
 
 
 
-    // Code below generated by codegen v1.0.21.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -71,7 +71,7 @@
             @NonNull String deviceMacAddress,
             @NonNull String packageName,
             @Nullable String deviceProfile,
-            boolean keepProfilePrivilegesWhenDeviceAway) {
+            boolean notifyOnDeviceNearby) {
         this.mUserId = userId;
         com.android.internal.util.AnnotationValidations.validate(
                 UserIdInt.class, null, mUserId);
@@ -82,7 +82,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
         this.mDeviceProfile = deviceProfile;
-        this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -103,8 +103,8 @@
     }
 
     @DataClass.Generated.Member
-    public boolean isKeepProfilePrivilegesWhenDeviceAway() {
-        return mKeepProfilePrivilegesWhenDeviceAway;
+    public boolean isNotifyOnDeviceNearby() {
+        return mNotifyOnDeviceNearby;
     }
 
     @Override
@@ -118,7 +118,7 @@
                 "deviceMacAddress = " + mDeviceMacAddress + ", " +
                 "packageName = " + mPackageName + ", " +
                 "deviceProfile = " + mDeviceProfile + ", " +
-                "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
+                "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby +
         " }";
     }
 
@@ -139,7 +139,7 @@
                 && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
                 && Objects.equals(mPackageName, that.mPackageName)
                 && Objects.equals(mDeviceProfile, that.mDeviceProfile)
-                && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
+                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby;
     }
 
     @Override
@@ -153,7 +153,7 @@
         _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
         _hash = 31 * _hash + Objects.hashCode(mPackageName);
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
-        _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
+        _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
         return _hash;
     }
 
@@ -164,7 +164,7 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
+        if (mNotifyOnDeviceNearby) flg |= 0x10;
         if (mDeviceProfile != null) flg |= 0x8;
         dest.writeByte(flg);
         dest.writeInt(mUserId);
@@ -185,7 +185,7 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
-        boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0;
+        boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
         int userId = in.readInt();
         String deviceMacAddress = in.readString();
         String packageName = in.readString();
@@ -201,7 +201,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
         this.mDeviceProfile = deviceProfile;
-        this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+        this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -221,10 +221,10 @@
     };
 
     @DataClass.Generated(
-            time = 1606940835778L,
-            codegenVersion = "1.0.21",
+            time = 1610482674799L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/companion/Association.java",
-            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mKeepProfilePrivilegesWhenDeviceAway\npublic  int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final  boolean mNotifyOnDeviceNearby\npublic  int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index d7dc86a..46d8900 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.system.Int32Ref;
+import android.system.Int64Ref;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -4050,7 +4050,7 @@
         // Convert to Point, since that's what the API is defined as
         final Bundle opts = new Bundle();
         opts.putParcelable(EXTRA_SIZE, new Point(size.getWidth(), size.getHeight()));
-        final Int32Ref orientation = new Int32Ref(0);
+        final Int64Ref orientation = new Int64Ref(0);
 
         Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
             final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c5206d7..bed42f4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -64,7 +64,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.StatFs;
-import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -3575,6 +3574,7 @@
             LIGHTS_SERVICE,
             //@hide: PEOPLE_SERVICE,
             //@hide: DEVICE_STATE_SERVICE,
+            //@hide: SPEECH_RECOGNITION_SERVICE,
             UWB_SERVICE,
             MEDIA_METRICS_SERVICE,
     })
@@ -4371,6 +4371,16 @@
     public static final String BIOMETRIC_SERVICE = "biometric";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.MediaCommunicationManager}
+     * for managing {@link android.media.MediaSession2}.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.MediaCommunicationManager
+     */
+    public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.media.MediaRouter} for controlling and managing
      * routing of media.
@@ -5410,6 +5420,14 @@
     public static final String MEDIA_METRICS_SERVICE = "media_metrics";
 
     /**
+     * Use with {@link #getSystemService(String)} to access system speech recognition service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+    */
+    public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
@@ -6469,7 +6487,7 @@
      * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
      * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
      * contexts throws {@link android.os.strictmode.Violation} if
-     * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
+     * {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
      * <p>
      * Examples of UI contexts are
      * an {@link android.app.Activity Activity}, a context created from
@@ -6479,7 +6497,7 @@
      *
      * @see #getDisplay()
      * @see #getSystemService(String)
-     * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
+     * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
      */
     public static boolean isUiContext(@NonNull Context context) {
         return context.isUiContext();
diff --git a/core/java/android/hardware/Battery.java b/core/java/android/hardware/Battery.java
new file mode 100644
index 0000000..24c8d76
--- /dev/null
+++ b/core/java/android/hardware/Battery.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.os.BatteryManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The Battery class is a representation of a single battery on a device.
+ */
+public abstract class Battery {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN,
+            STATUS_CHARGING,
+            STATUS_DISCHARGING,
+            STATUS_NOT_CHARGING,
+            STATUS_FULL
+    })
+    public @interface BatteryStatus {
+    }
+
+    /** Battery status is unknown. */
+    public static final int STATUS_UNKNOWN = BatteryManager.BATTERY_STATUS_UNKNOWN;
+    /** Battery is charging. */
+    public static final int STATUS_CHARGING = BatteryManager.BATTERY_STATUS_CHARGING;
+    /** Battery is discharging. */
+    public static final int STATUS_DISCHARGING = BatteryManager.BATTERY_STATUS_DISCHARGING;
+    /** Battery is connected to power but not charging. */
+    public static final int STATUS_NOT_CHARGING = BatteryManager.BATTERY_STATUS_NOT_CHARGING;
+    /** Battery is full. */
+    public static final int STATUS_FULL = BatteryManager.BATTERY_STATUS_FULL;
+
+    /**
+     * Check whether the hardware has a battery.
+     *
+     * @return True if the hardware has a battery, else false.
+     */
+    public abstract boolean hasBattery();
+
+    /**
+     * Get the battery status.
+     *
+     * @return the battery status.
+     */
+    public abstract @BatteryStatus int getStatus();
+
+    /**
+     * Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity
+     * Returns -1 when battery capacity can't be read.
+     *
+     * @return the battery capacity.
+     */
+    public abstract @FloatRange(from = -1.0f, to = 1.0f) float getCapacity();
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 4145a72..08b1e24 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -29,7 +29,6 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.os.RemoteException;
-import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.util.Slog;
 
@@ -47,6 +46,13 @@
     private static final String TAG = "BiometricManager";
 
     /**
+     * An ID that should match any biometric sensor on the device.
+     *
+     * @hide
+     */
+    public static final int SENSOR_ID_ANY = -1;
+
+    /**
      * No error detected.
      */
     public static final int BIOMETRIC_SUCCESS =
@@ -139,7 +145,7 @@
          *
          * <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
          *
-         * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+         * @see android.security.keystore.KeyGenParameterSpec.Builder
          */
         int BIOMETRIC_STRONG = 0x000F;
 
@@ -182,7 +188,7 @@
          * <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key
          * generation.
          *
-         * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+         * @see android.security.keystore.KeyGenParameterSpec.Builder
          */
         int DEVICE_CREDENTIAL = 1 << 15;
     }
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 76cf9b9..4f6a7c7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -36,7 +36,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.security.identity.IdentityCredential;
-import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.text.TextUtils;
 import android.util.Log;
@@ -325,7 +324,7 @@
          * request authentication with the proper set of authenticators (e.g. match the
          * authenticators specified during key generation).
          *
-         * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+         * @see android.security.keystore.KeyGenParameterSpec.Builder
          * @see KeyProperties#AUTH_BIOMETRIC_STRONG
          * @see KeyProperties#AUTH_DEVICE_CREDENTIAL
          *
@@ -365,6 +364,21 @@
         }
 
         /**
+         * If set, authenticate using the biometric sensor with the given ID.
+         *
+         * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default).
+         * @return This builder.
+         *
+         * @hide
+         */
+        @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+        @NonNull
+        public Builder setSensorId(int sensorId) {
+            mPromptInfo.setSensorId(sensorId);
+            return this;
+        }
+
+        /**
          * Creates a {@link BiometricPrompt}.
          *
          * @return An instance of {@link BiometricPrompt}.
@@ -589,7 +603,8 @@
      *
      * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
      * time-based. This is specified during key creation via the timeout parameter of the
-     * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API.
+     * {@code setUserAuthenticationParameters(int, int)} method of {@link
+     * android.security.keystore.KeyGenParameterSpec.Builder}.
      *
      * <p>CryptoObjects are used to unlock auth-per-use keys via
      * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
@@ -778,6 +793,27 @@
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AuthenticationCallback callback,
             int userId) {
+        authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */);
+    }
+
+    /**
+     * Authenticates for the given user and keystore operation.
+     *
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     * @param userId The user to authenticate
+     * @param operationId The keystore operation associated with authentication
+     *
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void authenticateUserForOperation(
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback,
+            int userId,
+            long operationId) {
         if (cancel == null) {
             throw new IllegalArgumentException("Must supply a cancellation signal");
         }
@@ -787,7 +823,7 @@
         if (callback == null) {
             throw new IllegalArgumentException("Must supply a callback");
         }
-        authenticateInternal(null /* crypto */, cancel, executor, callback, userId);
+        authenticateInternal(operationId, cancel, executor, callback, userId);
     }
 
     /**
@@ -912,11 +948,31 @@
         }
     }
 
-    private void authenticateInternal(@Nullable CryptoObject crypto,
+    private void authenticateInternal(
+            @Nullable CryptoObject crypto,
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AuthenticationCallback callback,
             int userId) {
+
+        mCryptoObject = crypto;
+        final long operationId = crypto != null ? crypto.getOpId() : 0L;
+        authenticateInternal(operationId, cancel, executor, callback, userId);
+    }
+
+    private void authenticateInternal(
+            long operationId,
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback,
+            int userId) {
+
+        // Ensure we don't return the wrong crypto object as an auth result.
+        if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) {
+            Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null");
+            mCryptoObject = null;
+        }
+
         try {
             if (cancel.isCanceled()) {
                 Log.w(TAG, "Authentication already canceled");
@@ -925,13 +981,11 @@
                 cancel.setOnCancelListener(new OnAuthenticationCancelListener());
             }
 
-            mCryptoObject = crypto;
             mExecutor = executor;
             mAuthenticationCallback = callback;
-            final long operationId = crypto != null ? crypto.getOpId() : 0;
 
             final PromptInfo promptInfo;
-            if (crypto != null) {
+            if (operationId != 0L) {
                 // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth.
                 // Note that we use a new PromptInfo here so as to not overwrite the application's
                 // preference, since it is possible that the same prompt configuration be used
@@ -952,10 +1006,9 @@
 
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception while authenticating", e);
-            mExecutor.execute(() -> {
-                callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        mContext.getString(R.string.biometric_error_hw_unavailable));
-            });
+            mExecutor.execute(() -> callback.onAuthenticationError(
+                    BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                    mContext.getString(R.string.biometric_error_hw_unavailable)));
         }
     }
 }
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index c2eff7d..0e99f31 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -40,6 +40,7 @@
     private @BiometricManager.Authenticators.Types int mAuthenticators;
     private boolean mDisallowBiometricsIfPolicyExists;
     private boolean mReceiveSystemEvents;
+    private int mSensorId = -1;
 
     public PromptInfo() {
 
@@ -59,6 +60,7 @@
         mAuthenticators = in.readInt();
         mDisallowBiometricsIfPolicyExists = in.readBoolean();
         mReceiveSystemEvents = in.readBoolean();
+        mSensorId = in.readInt();
     }
 
     public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -93,6 +95,7 @@
         dest.writeInt(mAuthenticators);
         dest.writeBoolean(mDisallowBiometricsIfPolicyExists);
         dest.writeBoolean(mReceiveSystemEvents);
+        dest.writeInt(mSensorId);
     }
 
     public boolean containsPrivateApiConfigurations() {
@@ -166,6 +169,10 @@
         mReceiveSystemEvents = receiveSystemEvents;
     }
 
+    public void setSensorId(int sensorId) {
+        mSensorId = sensorId;
+    }
+
     // Getters
 
     public CharSequence getTitle() {
@@ -226,4 +233,8 @@
     public boolean isReceiveSystemEvents() {
         return mReceiveSystemEvents;
     }
+
+    public int getSensorId() {
+        return mSensorId;
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d932865..66b9600 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -24,7 +24,6 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
@@ -56,8 +55,6 @@
 import android.util.Slog;
 import android.view.Surface;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.security.Signature;
 import java.util.ArrayList;
 import java.util.List;
@@ -98,13 +95,6 @@
      */
     public static final int SENSOR_ID_ANY = -1;
 
-    /**
-     * @hide
-     */
-    @IntDef({SENSOR_ID_ANY})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SensorId {}
-
     private IFingerprintService mService;
     private Context mContext;
     private IBinder mToken = new Binder();
@@ -508,8 +498,8 @@
      */
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId,
-            int userId) {
+            @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -653,15 +643,12 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void generateChallenge(int userId, GenerateChallengeCallback callback) {
-        final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
-                getSensorPropertiesInternal();
-        if (fingerprintSensorProperties.isEmpty()) {
+        final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+        if (sensorProps == null) {
             Slog.e(TAG, "No sensors");
             return;
         }
-
-        final int sensorId = fingerprintSensorProperties.get(0).sensorId;
-        generateChallenge(sensorId, userId, callback);
+        generateChallenge(sensorProps.sensorId, userId, callback);
     }
 
     /**
@@ -681,18 +668,18 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void revokeChallenge(int userId, long challenge) {
-        if (mService != null) try {
-            final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
-                    getSensorPropertiesInternal();
-            if (fingerprintSensorProperties.isEmpty()) {
-                Slog.e(TAG, "No sensors");
-                return;
+        if (mService != null) {
+            try {
+                final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+                if (sensorProps == null) {
+                    Slog.e(TAG, "No sensors");
+                    return;
+                }
+                mService.revokeChallenge(mToken, sensorProps.sensorId, userId,
+                        mContext.getOpPackageName(), challenge);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
             }
-            final int sensorId = fingerprintSensorProperties.get(0).sensorId;
-            mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
-                    challenge);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1161,6 +1148,12 @@
         }
     }
 
+    @Nullable
+    private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() {
+        final List<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal();
+        return allSensors.isEmpty() ? null : allSensors.get(0);
+    }
+
     private void cancelEnrollment() {
         if (mService != null) try {
             mService.cancelEnrollment(mToken);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index b39df4d..c69c47f 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -93,6 +93,10 @@
     int[] getVibratorIds(int deviceId);
     boolean isVibrating(int deviceId);
 
+    // Input device battery query.
+    int getBatteryStatus(int deviceId);
+    int getBatteryCapacity(int deviceId);
+
     void setPointerIconType(int typeId);
     void setCustomPointerIcon(in PointerIcon icon);
 
diff --git a/core/java/android/hardware/input/InputDeviceBattery.java b/core/java/android/hardware/input/InputDeviceBattery.java
new file mode 100644
index 0000000..0fe124e
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceBattery.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY;
+
+import android.hardware.Battery;
+
+/**
+ * Battery implementation for input devices.
+ *
+ * @hide
+ */
+public final class InputDeviceBattery extends Battery {
+    private static final float NULL_BATTERY_CAPACITY = -1.0f;
+
+    private final InputManager mInputManager;
+    private final int mDeviceId;
+    private final boolean mHasBattery;
+
+    InputDeviceBattery(InputManager inputManager, int deviceId, boolean hasBattery) {
+        mInputManager = inputManager;
+        mDeviceId = deviceId;
+        mHasBattery = hasBattery;
+    }
+
+    @Override
+    public boolean hasBattery() {
+        return mHasBattery;
+    }
+
+    @Override
+    public int getStatus() {
+        if (!mHasBattery) {
+            return BATTERY_STATUS_UNKNOWN;
+        }
+        return mInputManager.getBatteryStatus(mDeviceId);
+    }
+
+    @Override
+    public float getCapacity() {
+        if (mHasBattery) {
+            int capacity = mInputManager.getBatteryCapacity(mDeviceId);
+            if (capacity != INVALID_BATTERY_CAPACITY) {
+                return (float) capacity / 100.0f;
+            }
+        }
+        return NULL_BATTERY_CAPACITY;
+    }
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6ab1106..185c59d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -909,18 +910,17 @@
     }
 
     /**
-     * Returns the maximum allowed obscuring opacity by UID to propagate touches.
+     * Returns the maximum allowed obscuring opacity per UID to propagate touches.
      *
-     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
-     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
-     * above the touch-consuming window.
+     * <p>For certain window types (eg. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}), the decision
+     * of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring
+     * opacity of the windows above the touch-consuming window, per UID. Check documentation of
+     * {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details.
      *
-     * @see #setMaximumObscuringOpacityForTouch(Context, float)
-     *
-     * @hide
+     * @see LayoutParams#FLAG_NOT_TOUCHABLE
      */
-    @TestApi
-    public float getMaximumObscuringOpacityForTouch(@NonNull Context context) {
+    public float getMaximumObscuringOpacityForTouch() {
+        Context context = ActivityThread.currentApplication();
         return Settings.Global.getFloat(context.getContentResolver(),
                 Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
                 DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
@@ -946,17 +946,18 @@
      *
      * This value should be between 0 (inclusive) and 1 (inclusive).
      *
-     * @see #getMaximumObscuringOpacityForTouch(Context)
+     * @see #getMaximumObscuringOpacityForTouch()
      *
      * @hide
      */
     @TestApi
     @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
-    public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) {
+    public void setMaximumObscuringOpacityForTouch(float opacity) {
         if (opacity < 0 || opacity > 1) {
             throw new IllegalArgumentException(
                     "Maximum obscuring opacity for touch should be >= 0 and <= 1");
         }
+        Context context = ActivityThread.currentApplication();
         Settings.Global.putFloat(context.getContentResolver(),
                 Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
     }
@@ -1244,6 +1245,32 @@
     }
 
     /**
+     * Get the battery status of the input device
+     * @param deviceId The input device ID
+     * @hide
+     */
+    public int getBatteryStatus(int deviceId) {
+        try {
+            return mIm.getBatteryStatus(deviceId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the remaining battery capacity of the input device
+     * @param deviceId The input device ID
+     * @hide
+     */
+    public int getBatteryCapacity(int deviceId) {
+        try {
+            return mIm.getBatteryCapacity(deviceId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Add a runtime association between the input port and the display port. This overrides any
      * static associations.
      * @param inputPort The port of the input device.
@@ -1470,6 +1497,15 @@
     }
 
     /**
+     * Gets a battery object associated with an input device, assuming it has one.
+     * @return The battery, never null.
+     * @hide
+     */
+    public InputDeviceBattery getInputDeviceBattery(int deviceId, boolean hasBattery) {
+        return new InputDeviceBattery(this, deviceId, hasBattery);
+    }
+
+    /**
      * Listens for changes in input devices.
      */
     public interface InputDeviceListener {
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 33e33ee..9bfd75e 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -249,11 +249,13 @@
             // Nasty casework for the shadow calllog begins...
             // First see if we're just inserting for one user. If so, insert into the shadow
             // based on whether that user is unlocked.
-            if (user != null) {
-                Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI
+            UserHandle realUser = UserHandle.CURRENT.equals(user)
+                    ? android.os.Process.myUserHandle() : user;
+            if (realUser != null) {
+                Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI
                         : SHADOW_CALL_COMPOSER_PICTURE_URI;
                 Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri,
-                        user.getIdentifier());
+                        realUser.getIdentifier());
                 Log.i(LOG_TAG, "Inserting call composer for single user at "
                         + pictureInsertionUri);
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 79f055e..fbeb5de 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8055,6 +8055,15 @@
                 "one_handed_tutorial_show_count";
 
         /**
+         * Indicates whether transform is enabled.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
+         * @hide
+         */
+        public static final String TRANSFORM_ENABLED = "transform_enabled";
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -14721,9 +14730,8 @@
          *     touch, allow the UID to propagate the touch.
          * </ul>
          *
-         * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context)
-         * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context,
-         * float)
+         * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch()
+         * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(float)
          *
          * @hide
          */
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index e0f3018..44daeff 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -46,7 +46,7 @@
     void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
 
     // assistants only
-    void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
+    void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel, in NotificationRankingUpdate update);
     void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
     void onNotificationsSeen(in List<String> keys);
     void onPanelRevealed(int items);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index cf2152c..1d49a72 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -126,7 +126,7 @@
      * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
      *
      * @param sbn the new notification
-     * @return an adjustment or null to take no action, within 100ms.
+     * @return an adjustment or null to take no action, within 200ms.
      */
     abstract public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn);
 
@@ -135,7 +135,7 @@
      *
      * @param sbn the new notification
      * @param channel the channel the notification was posted to
-     * @return an adjustment or null to take no action, within 100ms.
+     * @return an adjustment or null to take no action, within 200ms.
      */
     public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn,
             @NonNull NotificationChannel channel) {
@@ -143,6 +143,20 @@
     }
 
     /**
+     * A notification was posted by an app. Called before post.
+     *
+     * @param sbn the new notification
+     * @param channel the channel the notification was posted to
+     * @param rankingMap The current ranking map that can be used to retrieve ranking information
+     *                   for active notifications.
+     * @return an adjustment or null to take no action, within 200ms.
+     */
+    public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn,
+            @NonNull NotificationChannel channel, @NonNull RankingMap rankingMap) {
+        return onNotificationEnqueued(sbn, channel);
+    }
+
+    /**
      * Implement this method to learn when notifications are removed, how they were interacted with
      * before removal, and why they were removed.
      * <p>
@@ -316,7 +330,7 @@
     private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
         @Override
         public void onNotificationEnqueuedWithChannel(IStatusBarNotificationHolder sbnHolder,
-                NotificationChannel channel) {
+                NotificationChannel channel, NotificationRankingUpdate update) {
             StatusBarNotification sbn;
             try {
                 sbn = sbnHolder.get();
@@ -330,9 +344,11 @@
                 return;
             }
 
+            applyUpdateLocked(update);
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = sbn;
             args.arg2 = channel;
+            args.arg3 = getCurrentRanking();
             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
                     args).sendToTarget();
         }
@@ -472,8 +488,9 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
                     NotificationChannel channel = (NotificationChannel) args.arg2;
+                    RankingMap ranking = (RankingMap) args.arg3;
                     args.recycle();
-                    Adjustment adjustment = onNotificationEnqueued(sbn, channel);
+                    Adjustment adjustment = onNotificationEnqueued(sbn, channel, ranking);
                     setAdjustmentIssuer(adjustment);
                     if (adjustment != null) {
                         if (!isBound()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c41e599..64cddc3 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1431,7 +1431,8 @@
 
         @Override
         public void onNotificationEnqueuedWithChannel(
-                IStatusBarNotificationHolder notificationHolder, NotificationChannel channel)
+                IStatusBarNotificationHolder notificationHolder, NotificationChannel channel,
+                NotificationRankingUpdate update)
                 throws RemoteException {
             // no-op in the listener
         }
diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl
new file mode 100644
index 0000000..7158ba2
--- /dev/null
+++ b/core/java/android/speech/IRecognitionServiceManager.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 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.speech;
+
+import android.speech.IRecognitionServiceManagerCallback;
+
+/**
+ * Binder service allowing speech recognition proxied by the system.
+ *
+ * {@hide}
+ */
+interface IRecognitionServiceManager {
+    void createSession(in IRecognitionServiceManagerCallback callback);
+}
diff --git a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
new file mode 100644
index 0000000..d760810
--- /dev/null
+++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 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.speech;
+
+import android.speech.IRecognitionService;
+
+/**
+ * Callback for the service allowing speech recognition proxied by the system.
+ *
+ * {@hide}
+ */
+oneway interface IRecognitionServiceManagerCallback {
+    void onSuccess(in IRecognitionService service);
+    void onError();
+}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 5fd192a..c97dbfe 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -50,7 +51,9 @@
     /**
      * Name under which a RecognitionService component publishes information about itself.
      * This meta-data should reference an XML resource containing a
-     * <code>&lt;{@link android.R.styleable#RecognitionService recognition-service}&gt;</code> tag.
+     * <code>&lt;{@link android.R.styleable#RecognitionService recognition-service}&gt;</code> or
+     * <code>&lt;{@link android.R.styleable#RecognitionService on-device-recognition-service}
+     * &gt;</code> tag.
      */
     public static final String SERVICE_META_DATA = "android.speech";
 
@@ -182,6 +185,13 @@
     private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery,
             @NonNull String packageName, @Nullable String featureId) {
         if (DBG) Log.d(TAG, "checkPermissions");
+
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SYSTEM_UID) {
+            // Assuming system has verified permissions of the caller.
+            return true;
+        }
+
         if (forDataDelivery) {
             if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
                     android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
@@ -342,6 +352,7 @@
          * Return the Linux uid assigned to the process that sent you the current transaction that
          * is being processed. This is obtained from {@link Binder#getCallingUid()}.
          */
+        // TODO(b/176578753): need to make sure this is fixed when proxied through system.
         public int getCallingUid() {
             return mCallingUid;
         }
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index aea94bf..de879c6 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -16,6 +16,7 @@
 
 package android.speech;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +28,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -38,8 +40,9 @@
 /**
  * This class provides access to the speech recognition service. This service allows access to the
  * speech recognizer. Do not instantiate this class directly, instead, call
- * {@link SpeechRecognizer#createSpeechRecognizer(Context)}. This class's methods must be
- * invoked only from the main application thread. 
+ * {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or
+ * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
+ * invoked only from the main application thread.
  *
  * <p>The implementation of this API is likely to stream audio to remote servers to perform speech
  * recognition. As such this API is not intended to be used for continuous recognition, which would
@@ -122,8 +125,13 @@
     /** Component to direct service intent to */
     private final ComponentName mServiceComponent;
 
+    /** Whether to use on-device speech recognizer. */
+    private final boolean mOnDevice;
+
+    private IRecognitionServiceManager mManagerService;
+
     /** Handler that will execute the main tasks */
-    private Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -159,6 +167,17 @@
     private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
         mContext = context;
         mServiceComponent = serviceComponent;
+        mOnDevice = false;
+    }
+
+    /**
+     * The right way to create a {@code SpeechRecognizer} is by using
+     * {@link #createOnDeviceSpeechRecognizer} static factory method
+     */
+    private SpeechRecognizer(final Context context, boolean onDevice) {
+        mContext = context;
+        mServiceComponent = null;
+        mOnDevice = onDevice;
     }
 
     /**
@@ -194,6 +213,7 @@
      * @return {@code true} if recognition is available, {@code false} otherwise
      */
     public static boolean isRecognitionAvailable(final Context context) {
+        // TODO(b/176578753): make sure this works well with system speech recognizers.
         final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
                 new Intent(RecognitionService.SERVICE_INTERFACE), 0);
         return list != null && list.size() != 0;
@@ -231,13 +251,32 @@
     public static SpeechRecognizer createSpeechRecognizer(final Context context,
             final ComponentName serviceComponent) {
         if (context == null) {
-            throw new IllegalArgumentException("Context cannot be null)");
+            throw new IllegalArgumentException("Context cannot be null");
         }
         checkIsCalledFromMainThread();
         return new SpeechRecognizer(context, serviceComponent);
     }
 
     /**
+     * Factory method to create a new {@code SpeechRecognizer}.
+     *
+     * <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called
+     * before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
+     * notifications will be received.
+     *
+     * @param context in which to create {@code SpeechRecognizer}
+     * @return a new on-device {@code SpeechRecognizer}.
+     */
+    @NonNull
+    public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("Context cannot be null");
+        }
+        checkIsCalledFromMainThread();
+        return new SpeechRecognizer(context, /* onDevice */ true);
+    }
+
+    /**
      * Sets the listener that will receive all the callbacks. The previous unfinished commands will
      * be executed with the old listener, while any following command will be executed with the new
      * listener.
@@ -265,36 +304,74 @@
         }
         checkIsCalledFromMainThread();
         if (mConnection == null) { // first time connection
-            mConnection = new Connection();
-            
-            Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
-            
-            if (mServiceComponent == null) {
-                String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
-                        Settings.Secure.VOICE_RECOGNITION_SERVICE);
-                
-                if (TextUtils.isEmpty(serviceComponent)) {
-                    Log.e(TAG, "no selected voice recognition service");
-                    mListener.onError(ERROR_CLIENT);
-                    return;
-                }
-                
-                serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent));                
+            // TODO(b/176578753): both flows should go through system service.
+            if (mOnDevice) {
+                connectToSystemService();
             } else {
-                serviceIntent.setComponent(mServiceComponent);
-            }
-            if (!mContext.bindService(serviceIntent, mConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
-                Log.e(TAG, "bind to recognition service failed");
-                mConnection = null;
-                mService = null;
-                mListener.onError(ERROR_CLIENT);
-                return;
+                connectToService();
             }
         }
         putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
     }
 
+    private void connectToSystemService() {
+        mManagerService = IRecognitionServiceManager.Stub.asInterface(
+                ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
+
+        if (mManagerService == null) {
+            mListener.onError(ERROR_CLIENT);
+            return;
+        }
+
+        try {
+            // TODO(b/176578753): this has to supply information on whether to use on-device impl.
+            mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){
+                @Override
+                public void onSuccess(IRecognitionService service) throws RemoteException {
+                    mService = service;
+                }
+
+                @Override
+                public void onError() throws RemoteException {
+                    Log.e(TAG, "Bind to system recognition service failed");
+                    mListener.onError(ERROR_CLIENT);
+                }
+            });
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    private void connectToService() {
+        mConnection = new Connection();
+
+        Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
+
+        if (mServiceComponent == null) {
+            String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.VOICE_RECOGNITION_SERVICE);
+
+            if (TextUtils.isEmpty(serviceComponent)) {
+                Log.e(TAG, "no selected voice recognition service");
+                mListener.onError(ERROR_CLIENT);
+                return;
+            }
+
+            serviceIntent.setComponent(
+                    ComponentName.unflattenFromString(serviceComponent));
+        } else {
+            serviceIntent.setComponent(mServiceComponent);
+        }
+        if (!mContext.bindService(serviceIntent, mConnection,
+                Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
+            Log.e(TAG, "bind to recognition service failed");
+            mConnection = null;
+            mService = null;
+            mListener.onError(ERROR_CLIENT);
+            return;
+        }
+    }
+
     /**
      * Stops listening for speech. Speech captured so far will be recognized as if the user had
      * stopped speaking at this point. Note that in the default case, this does not need to be
@@ -378,7 +455,7 @@
             mListener.onError(ERROR_CLIENT);
         }
     }
-    
+
     private boolean checkOpenConnection() {
         if (mService != null) {
             return true;
@@ -433,7 +510,7 @@
         private final static int MSG_RMS_CHANGED = 8;
         private final static int MSG_ON_EVENT = 9;
 
-        private final Handler mInternalHandler = new Handler() {
+        private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
             @Override
             public void handleMessage(Message msg) {
                 if (mInternalListener == null) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f4b90e1..bc03222 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,6 +22,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.hardware.Battery;
 import android.hardware.SensorManager;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
@@ -73,6 +74,7 @@
     private final boolean mHasMicrophone;
     private final boolean mHasButtonUnderPad;
     private final boolean mHasSensor;
+    private final boolean mHasBattery;
     private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
     @GuardedBy("mMotionRanges")
@@ -84,6 +86,9 @@
     @GuardedBy("mMotionRanges")
     private SensorManager mSensorManager;
 
+    @GuardedBy("mMotionRanges")
+    private Battery mBattery;
+
     /**
      * A mask for input source classes.
      *
@@ -323,6 +328,13 @@
     public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
 
     /**
+     * The input source is a sensor associated with the input device.
+     *
+     * @see #SOURCE_CLASS_NONE
+     */
+    public static final int SOURCE_SENSOR = 0x04000000 | SOURCE_CLASS_NONE;
+
+    /**
      * A special input source constant that is used when filtering input devices
      * to match devices that provide any type of input source.
      */
@@ -448,7 +460,7 @@
     public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
             int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
             KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone,
-            boolean hasButtonUnderPad, boolean hasSensor) {
+            boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery) {
         mId = id;
         mGeneration = generation;
         mControllerNumber = controllerNumber;
@@ -464,6 +476,7 @@
         mHasMicrophone = hasMicrophone;
         mHasButtonUnderPad = hasButtonUnderPad;
         mHasSensor = hasSensor;
+        mHasBattery = hasBattery;
         mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
     }
 
@@ -483,6 +496,7 @@
         mHasMicrophone = in.readInt() != 0;
         mHasButtonUnderPad = in.readInt() != 0;
         mHasSensor = in.readInt() != 0;
+        mHasBattery = in.readInt() != 0;
         mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
 
         int numRanges = in.readInt();
@@ -830,6 +844,22 @@
     }
 
     /**
+     * Gets the battery object associated with the device, if there is one.
+     * Even if the device does not have a battery, the result is never null.
+     * Use {@link Battery#hasBattery} to determine whether a battery is
+     * present.
+     *
+     * @return The battery object associated with the device, never null.
+     */
+    @NonNull
+    public Battery getBattery() {
+        if (mBattery == null) {
+            mBattery = InputManager.getInstance().getInputDeviceBattery(mId, mHasBattery);
+        }
+        return mBattery;
+    }
+
+    /**
      * Gets the sensor manager service associated with the input device.
      * Even if the device does not have a sensor, the result is never null.
      * Use {@link SensorManager#getSensorList} to get a full list of all supported sensors.
@@ -1051,6 +1081,7 @@
         out.writeInt(mHasMicrophone ? 1 : 0);
         out.writeInt(mHasButtonUnderPad ? 1 : 0);
         out.writeInt(mHasSensor ? 1 : 0);
+        out.writeInt(mHasBattery ? 1 : 0);
 
         final int numRanges = mMotionRanges.size();
         out.writeInt(numRanges);
@@ -1097,6 +1128,8 @@
 
         description.append("  Has Sensor: ").append(mHasSensor).append("\n");
 
+        description.append("  Has battery: ").append(mHasBattery).append("\n");
+
         description.append("  Has mic: ").append(mHasMicrophone).append("\n");
 
         description.append("  Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1327f9c..6edae93 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1555,17 +1555,26 @@
          *   <li><b>Fully transparent windows</b>: This window has {@link LayoutParams#alpha} equal
          *   to 0.
          *   <li><b>One SAW window with enough transparency</b>: This window is of type {@link
-         *   #TYPE_APPLICATION_OVERLAY}, has {@link LayoutParams#alpha} below or equal to <b>0.8</b>
-         *   and it's the <b>only</b> window of type {@link #TYPE_APPLICATION_OVERLAY} from this UID
-         *   in the touch path.
+         *   #TYPE_APPLICATION_OVERLAY}, has {@link LayoutParams#alpha} below or equal to the
+         *   <a href="#MaximumOpacity">maximum obscuring opacity</a> (see below) and it's the
+         *   <b>only</b> window of type {@link #TYPE_APPLICATION_OVERLAY} from this UID in the touch
+         *   path.
          *   <li><b>Multiple SAW windows with enough transparency</b>: The multiple overlapping
          *   {@link #TYPE_APPLICATION_OVERLAY} windows in the
          *   touch path from this UID have a <b>combined obscuring opacity</b> below or equal to
-         *   <b>0.8</b>. See section below on how to compute this value.
+         *   the <a href="#MaximumOpacity">maximum obscuring opacity</a>. See section
+         *   <a href="#ObscuringOpacity">Combined obscuring opacity</a> below on how to compute this
+         *   value.
          * </ol>
          * <p>If none of these cases hold, the touch will not be delivered and a message will be
          * logged to logcat.</p>
          *
+         * <a name="MaximumOpacity"></a>
+         * <h3>Maximum obscuring opacity</h3>
+         * <p>This value is <b>0.8</b>. Apps that want to gather this value from the system rather
+         * than hard-coding it might want to use {@link
+         * android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch()}.</p>
+         *
          * <a name="ObscuringOpacity"></a>
          * <h3>Combined obscuring opacity</h3>
          *
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index a21c68b..fcf8bb4 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -41,7 +41,6 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseLongArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
@@ -379,6 +378,7 @@
         mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
                 * mPowerProfile.getBatteryCapacity()) / 100;
 
+        // Create list of (almost all) sippers, calculate their usage, and put them in mUsageList.
         processAppUsage(asUsers);
 
         Collections.sort(mUsageList);
@@ -556,8 +556,7 @@
     }
 
     /**
-     * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on
-     * foreground activity time.
+     * Mark the {@link BatterySipper} that we should hide.
      *
      * @param sippers sipper list that need to check and remove
      * @return the total power of the hidden items of {@link BatterySipper}
@@ -565,7 +564,6 @@
      */
     public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
         double proportionalSmearPowerMah = 0;
-        BatterySipper screenSipper = null;
         for (int i = sippers.size() - 1; i >= 0; i--) {
             final BatterySipper sipper = sippers.get(i);
             sipper.shouldHide = shouldHideSipper(sipper);
@@ -581,45 +579,11 @@
                     proportionalSmearPowerMah += sipper.totalPowerMah;
                 }
             }
-
-            if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
-                screenSipper = sipper;
-            }
         }
-
-        smearScreenBatterySipper(sippers, screenSipper);
-
         return proportionalSmearPowerMah;
     }
 
     /**
-     * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
-     * time.
-     */
-    public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
-        long totalActivityTimeMs = 0;
-        final SparseLongArray activityTimeArray = new SparseLongArray();
-        for (int i = 0, size = sippers.size(); i < size; i++) {
-            final BatteryStats.Uid uid = sippers.get(i).uidObj;
-            if (uid != null) {
-                final long timeMs = getProcessForegroundTimeMs(uid,
-                        BatteryStats.STATS_SINCE_CHARGED);
-                activityTimeArray.put(uid.getUid(), timeMs);
-                totalActivityTimeMs += timeMs;
-            }
-        }
-
-        if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
-            final double screenPowerMah = screenSipper.totalPowerMah;
-            for (int i = 0, size = sippers.size(); i < size; i++) {
-                final BatterySipper sipper = sippers.get(i);
-                sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
-                        / totalActivityTimeMs;
-            }
-        }
-    }
-
-    /**
      * Check whether we should hide the battery sipper.
      */
     public boolean shouldHideSipper(BatterySipper sipper) {
@@ -682,33 +646,6 @@
     }
 
     @VisibleForTesting
-    public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
-        final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
-        if (timer != null) {
-            return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
-        }
-
-        return 0;
-    }
-
-    @VisibleForTesting
-    public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
-        final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
-        final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP};
-
-        long timeUs = 0;
-        for (int type : foregroundTypes) {
-            final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which);
-            timeUs += localTime;
-        }
-
-        // Return the min value of STATE_TOP time and foreground activity time, since both of these
-        // time have some errors.
-        return convertUsToMs(
-                Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)));
-    }
-
-    @VisibleForTesting
     public void setPackageManager(PackageManager packageManager) {
         mPackageManager = packageManager;
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d475c65..2b034b0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1099,16 +1099,6 @@
     private long[] mCpuFreqs;
 
     /**
-     * Times spent by the system server process grouped by cluster and CPU speed.
-     */
-    private LongSamplingCounterArray mSystemServerCpuTimesUs;
-
-    /**
-     * Times spent by the system server threads grouped by cluster and CPU speed.
-     */
-    private LongSamplingCounterArray mSystemServerThreadCpuTimesUs;
-
-    /**
      * Times spent by the system server threads handling incoming binder requests.
      */
     private LongSamplingCounterArray mBinderThreadCpuTimesUs;
@@ -10856,6 +10846,14 @@
         }
     }
 
+    /**
+     * Starts tracking CPU time-in-state for threads of the system server process,
+     * keeping a separate account of threads receiving incoming binder calls.
+     */
+    public void startTrackingSystemServerCpuTime() {
+        mSystemServerCpuThreadReader.startTrackingThreadCpuTime();
+    }
+
     public void setCallback(BatteryCallback cb) {
         mCallback = cb;
     }
@@ -11508,8 +11506,6 @@
 
         MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats);
 
-        resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs);
-        resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs);
         resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
 
         mLastHistoryStepDetails = null;
@@ -12695,27 +12691,17 @@
             return;
         }
 
-        if (mSystemServerCpuTimesUs == null) {
-            mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
-            mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+        if (mBinderThreadCpuTimesUs == null) {
             mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
         }
-        mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs);
-        mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs);
         mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
 
         if (DEBUG_BINDER_STATS) {
-            Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
-            long totalCpuTimeMs = 0;
-            long totalThreadTimeMs = 0;
+            Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)");
             long binderThreadTimeMs = 0;
             int cpuIndex = 0;
-            final long[] systemServerCpuTimesUs =
-                    mSystemServerCpuTimesUs.getCountsLocked(0);
-            final long[] systemServerThreadCpuTimesUs =
-                    mSystemServerThreadCpuTimesUs.getCountsLocked(0);
-            final long[] binderThreadCpuTimesUs =
-                    mBinderThreadCpuTimesUs.getCountsLocked(0);
+            final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
+                    BatteryStats.STATS_SINCE_CHARGED);
             int index = 0;
             int numCpuClusters = mPowerProfile.getNumCpuClusters();
             for (int cluster = 0; cluster < numCpuClusters; cluster++) {
@@ -12726,28 +12712,15 @@
                     if (speed != 0) {
                         sb.append(", ");
                     }
-                    long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000;
                     long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
-                    sb.append(String.format("%d/%d(%.1f%%)",
-                            binderCountMs,
-                            totalCountMs,
-                            totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0));
+                    sb.append(TextUtils.formatSimple("%10d", binderCountMs));
 
-                    totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000;
-                    totalThreadTimeMs += totalCountMs;
                     binderThreadTimeMs += binderCountMs;
                     index++;
                 }
                 cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
                 Slog.d(TAG, sb.toString());
             }
-
-            Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs);
-            Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs);
-            Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
-                    binderThreadTimeMs,
-                    binderThreadTimeMs != 0
-                            ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0));
         }
     }
 
@@ -14022,60 +13995,16 @@
     }
 
 
+    /**
+     * Estimates the time spent by the system server handling incoming binder requests.
+     */
     @Override
     public long[] getSystemServiceTimeAtCpuSpeeds() {
-        // Estimates the time spent by the system server handling incoming binder requests.
-        //
-        // The data that we can get from the kernel is this:
-        //   - CPU duration for a (thread - cluster - CPU speed) combination
-        //   - CPU duration for a (UID - cluster - CPU speed) combination
-        //
-        // The configuration we have in the Power Profile is this:
-        //   - Average CPU power for a (cluster - CPU speed) combination.
-        //
-        // The model used by BatteryStats can be illustrated with this example:
-        //
-        // - Let's say the system server has 10 threads.
-        // - These 10 threads spent 1000 ms of CPU time in aggregate
-        // - Of the 10 threads 4 were execute exclusively incoming binder calls.
-        // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
-        // - The real time spent by the system server process doing all of this is, say, 200 ms.
-        //
-        // We will assume that power consumption is proportional to the time spent by the CPU
-        // across all threads.  This is a crude assumption, but we don't have more detailed data.
-        // Thus,
-        //   binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime
-        //
-        // In our example,
-        //   binderRealTime = 200 * 600 / 1000 = 120ms
-        //
-        // We can then multiply this estimated time by the average power to obtain an estimate
-        // of the total power consumed by incoming binder calls for the given cluster/speed
-        // combination.
-
-        if (mSystemServerCpuTimesUs == null) {
+        if (mBinderThreadCpuTimesUs == null) {
             return null;
         }
 
-        final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked(
-                BatteryStats.STATS_SINCE_CHARGED);
-        final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked(
-                BatteryStats.STATS_SINCE_CHARGED);
-        final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
-                BatteryStats.STATS_SINCE_CHARGED);
-
-        final int size = systemServerCpuTimesUs.length;
-        final long[] results = new long[size];
-
-        for (int i = 0; i < size; i++) {
-            if (systemServerThreadCpuTimesUs[i] == 0) {
-                continue;
-            }
-
-            results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i]
-                    / systemServerThreadCpuTimesUs[i];
-        }
-        return results;
+        return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED);
     }
 
     /**
@@ -14506,7 +14435,7 @@
         }
 
         updateSystemServiceCallStats();
-        if (mSystemServerThreadCpuTimesUs != null) {
+        if (mBinderThreadCpuTimesUs != null) {
             pw.println("Per UID System server binder time in ms:");
             long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
             for (int i = 0; i < size; i++) {
@@ -16097,9 +16026,6 @@
             mUidStats.append(uid, u);
         }
 
-        mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
-        mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in,
-                mOnBatteryTimeBase);
         mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
     }
 
@@ -16308,8 +16234,6 @@
         } else {
             out.writeInt(0);
         }
-        LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs);
-        LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs);
         LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs);
     }
 
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index e6a9623..4d2a08a 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -16,23 +16,12 @@
 
 package com.android.internal.os;
 
-import static android.os.Process.PROC_OUT_LONG;
-import static android.os.Process.PROC_SPACE_TERM;
-
 import android.annotation.Nullable;
-import android.os.Process;
-import android.system.Os;
-import android.system.OsConstants;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
-import java.nio.file.DirectoryIteratorException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Arrays;
 
 /**
@@ -45,93 +34,65 @@
     private static final String TAG = "KernelSingleProcCpuThreadRdr";
 
     private static final boolean DEBUG = false;
-    private static final boolean NATIVE_ENABLED = true;
-
-    /**
-     * The name of the file to read CPU statistics from, must be found in {@code
-     * /proc/$PID/task/$TID}
-     */
-    private static final String CPU_STATISTICS_FILENAME = "time_in_state";
-
-    private static final String PROC_STAT_FILENAME = "stat";
-
-    /** Directory under /proc/$PID containing CPU stats files for threads */
-    public static final String THREAD_CPU_STATS_DIRECTORY = "task";
-
-    /** Default mount location of the {@code proc} filesystem */
-    private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
-
-    /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
-    private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
-
-    /** See https://man7.org/linux/man-pages/man5/proc.5.html */
-    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM | PROC_OUT_LONG,                  // 14: utime
-            PROC_SPACE_TERM | PROC_OUT_LONG,                  // 15: stime
-            // Ignore remaining fields
-    };
-
-    private final long[] mProcessFullStatsData = new long[2];
-
-    private static final int PROCESS_FULL_STAT_UTIME = 0;
-    private static final int PROCESS_FULL_STAT_STIME = 1;
-
-    /** Used to read and parse {@code time_in_state} files */
-    private final ProcTimeInStateReader mProcTimeInStateReader;
 
     private final int mPid;
 
-    /** Where the proc filesystem is mounted */
-    private final Path mProcPath;
+    private final CpuTimeInStateReader mCpuTimeInStateReader;
 
-    // How long a CPU jiffy is in milliseconds.
-    private final long mJiffyMillis;
-
-    // Path: /proc/<pid>/stat
-    private final String mProcessStatFilePath;
-
-    // Path: /proc/<pid>/task
-    private final Path mThreadsDirectoryPath;
+    private int[] mSelectedThreadNativeTids = new int[0];  // Sorted
 
     /**
-     * Count of frequencies read from the {@code time_in_state} file. Read from {@link
-     * #mProcTimeInStateReader#getCpuFrequenciesKhz()}.
+     * Count of frequencies read from the {@code time_in_state} file.
      */
     private int mFrequencyCount;
 
+    private boolean mIsTracking;
+
+    /**
+     * A CPU time-in-state provider for testing.  Imitates the behavior of the corresponding
+     * methods in frameworks/native/libs/cputimeinstate/cputimeinstate.c
+     */
+    @VisibleForTesting
+    public interface CpuTimeInStateReader {
+        /**
+         * Returns the overall number of cluster-frequency combinations.
+         */
+        int getCpuFrequencyCount();
+
+        /**
+         * Returns true to indicate success.
+         *
+         * Called from native.
+         */
+        boolean startTrackingProcessCpuTimes(int tgid);
+
+        /**
+         * Returns true to indicate success.
+         *
+         * Called from native.
+         */
+        boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey);
+
+        /**
+         * Must return an array of strings formatted like this:
+         * "aggKey:t0_0 t0_1...:t1_0 t1_1..."
+         * Times should be provided in nanoseconds.
+         *
+         * Called from native.
+         */
+        String[] getAggregatedTaskCpuFreqTimes(int pid);
+    }
+
     /**
      * Create with a path where `proc` is mounted. Used primarily for testing
      *
      * @param pid      PID of the process whose threads are to be read.
-     * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
      */
     @VisibleForTesting
-    public KernelSingleProcessCpuThreadReader(
-            int pid,
-            Path procPath) throws IOException {
+    public KernelSingleProcessCpuThreadReader(int pid,
+            @Nullable CpuTimeInStateReader cpuTimeInStateReader) throws IOException {
         mPid = pid;
-        mProcPath = procPath;
-        mProcTimeInStateReader = new ProcTimeInStateReader(
-                mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH));
-        long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
-        mJiffyMillis = 1000 / jiffyHz;
-        mProcessStatFilePath =
-                mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString();
-        mThreadsDirectoryPath =
-                mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY);
+        mCpuTimeInStateReader = cpuTimeInStateReader;
     }
 
     /**
@@ -142,7 +103,7 @@
     @Nullable
     public static KernelSingleProcessCpuThreadReader create(int pid) {
         try {
-            return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH);
+            return new KernelSingleProcessCpuThreadReader(pid, null);
         } catch (IOException e) {
             Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e);
             return null;
@@ -150,146 +111,98 @@
     }
 
     /**
-     * Get the CPU frequencies that correspond to the times reported in {@link
-     * ProcessCpuUsage#processCpuTimesMillis} etc.
+     * Starts tracking aggregated CPU time-in-state of all threads of the process with the PID
+     * supplied in the constructor.
+     */
+    public void startTrackingThreadCpuTimes() {
+        if (!mIsTracking) {
+            if (!startTrackingProcessCpuTimes(mPid, mCpuTimeInStateReader)) {
+                Slog.e(TAG, "Failed to start tracking process CPU times for " + mPid);
+            }
+            if (mSelectedThreadNativeTids.length > 0) {
+                if (!startAggregatingThreadCpuTimes(mSelectedThreadNativeTids,
+                        mCpuTimeInStateReader)) {
+                    Slog.e(TAG, "Failed to start tracking aggregated thread CPU times for "
+                            + Arrays.toString(mSelectedThreadNativeTids));
+                }
+            }
+            mIsTracking = true;
+        }
+    }
+
+    /**
+     * @param nativeTids an array of native Thread IDs whose CPU times should
+     *                   be aggregated as a group.  This is expected to be a subset
+     *                   of all thread IDs owned by the process.
+     */
+    public void setSelectedThreadIds(int[] nativeTids) {
+        mSelectedThreadNativeTids = nativeTids.clone();
+        if (mIsTracking) {
+            startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, mCpuTimeInStateReader);
+        }
+    }
+
+    /**
+     * Get the CPU frequencies that correspond to the times reported in {@link ProcessCpuUsage}.
      */
     public int getCpuFrequencyCount() {
         if (mFrequencyCount == 0) {
-            mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length;
+            mFrequencyCount = getCpuFrequencyCount(mCpuTimeInStateReader);
         }
         return mFrequencyCount;
     }
 
     /**
-     * Get the total and per-thread CPU usage of the process with the PID specified in the
-     * constructor.
-     *
-     * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should
-     *                          be aggregated as a group.  This is expected to be a subset
-     *                          of all thread IDs owned by the process.
+     * Get the total CPU usage of the process with the PID specified in the
+     * constructor. The CPU usage time is aggregated across all threads and may
+     * exceed the time the entire process has been running.
      */
     @Nullable
-    public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) {
+    public ProcessCpuUsage getProcessCpuUsage() {
         if (DEBUG) {
-            Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID "
-                    + mPid);
+            Slog.d(TAG, "Reading CPU thread usages for PID " + mPid);
         }
 
-        int cpuFrequencyCount = getCpuFrequencyCount();
-        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
+        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(getCpuFrequencyCount());
 
-        if (NATIVE_ENABLED) {
-            boolean result = readProcessCpuUsage(mProcPath.toString(), mPid,
-                    selectedThreadIds, processCpuUsage.processCpuTimesMillis,
-                    processCpuUsage.threadCpuTimesMillis,
-                    processCpuUsage.selectedThreadCpuTimesMillis);
-            if (!result) {
-                return null;
-            }
-            return processCpuUsage;
-        }
-
-        if (!isSorted(selectedThreadIds)) {
-            throw new IllegalArgumentException("selectedThreadIds is not sorted: "
-                    + Arrays.toString(selectedThreadIds));
-        }
-
-        if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null,
-                mProcessFullStatsData, null)) {
-            Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath);
+        boolean result = readProcessCpuUsage(mPid,
+                processCpuUsage.threadCpuTimesMillis,
+                processCpuUsage.selectedThreadCpuTimesMillis,
+                mCpuTimeInStateReader);
+        if (!result) {
             return null;
         }
 
-        long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME];
-        long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME];
-
-        long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
-
-        try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
-            for (Path threadDirectory : threadPaths) {
-                readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory);
-            }
-        } catch (IOException | DirectoryIteratorException e) {
-            // Expected when a process finishes
-            return null;
-        }
-
-        // Estimate per cluster per frequency CPU time for the entire process
-        // by distributing the total process CPU time proportionately to how much
-        // CPU time its threads took on those clusters/frequencies.  This algorithm
-        // works more accurately when when we have equally distributed concurrency.
-        // TODO(b/169279846): obtain actual process CPU times from the kernel
-        long totalCpuTimeAllThreads = 0;
-        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
-            totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i];
-        }
-
-        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
-            processCpuUsage.processCpuTimesMillis[i] =
-                    processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i]
-                            / totalCpuTimeAllThreads;
+        if (DEBUG) {
+            Slog.d(TAG, "threadCpuTimesMillis = "
+                    + Arrays.toString(processCpuUsage.threadCpuTimesMillis));
+            Slog.d(TAG, "selectedThreadCpuTimesMillis = "
+                    + Arrays.toString(processCpuUsage.selectedThreadCpuTimesMillis));
         }
 
         return processCpuUsage;
     }
 
-    /**
-     * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times.
-     *
-     * @param threadDirectory the {@code /proc} directory of the thread
-     */
-    private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds,
-            Path threadDirectory) {
-        // Get the thread ID from the directory name
-        final int threadId;
-        try {
-            final String directoryName = threadDirectory.getFileName().toString();
-            threadId = Integer.parseInt(directoryName);
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
-            return;
-        }
-
-        // Get the CPU statistics from the directory
-        final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
-        final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
-        if (cpuUsages == null) {
-            return;
-        }
-
-        final int cpuFrequencyCount = getCpuFrequencyCount();
-        final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0;
-        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
-            processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i];
-            if (isSelectedThread) {
-                processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i];
-            }
-        }
-    }
-
     /** CPU usage of a process, all of its threads and a selected subset of its threads */
     public static class ProcessCpuUsage {
-        public long[] processCpuTimesMillis;
         public long[] threadCpuTimesMillis;
         public long[] selectedThreadCpuTimesMillis;
 
         public ProcessCpuUsage(int cpuFrequencyCount) {
-            processCpuTimesMillis = new long[cpuFrequencyCount];
             threadCpuTimesMillis = new long[cpuFrequencyCount];
             selectedThreadCpuTimesMillis = new long[cpuFrequencyCount];
         }
     }
 
-    private static boolean isSorted(int[] array) {
-        for (int i = 0; i < array.length - 1; i++) {
-            if (array[i] > array[i + 1]) {
-                return false;
-            }
-        }
-        return true;
-    }
+    private native int getCpuFrequencyCount(CpuTimeInStateReader reader);
 
-    private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds,
-            long[] processCpuTimesMillis, long[] threadCpuTimesMillis,
-            long[] selectedThreadCpuTimesMillis);
+    private native boolean startTrackingProcessCpuTimes(int pid, CpuTimeInStateReader reader);
+
+    private native boolean startAggregatingThreadCpuTimes(int[] selectedThreadIds,
+            CpuTimeInStateReader reader);
+
+    private native boolean readProcessCpuUsage(int pid,
+            long[] threadCpuTimesMillis,
+            long[] selectedThreadCpuTimesMillis,
+            CpuTimeInStateReader reader);
 }
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 25f6b4d..9c4a267 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -21,9 +21,14 @@
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.SystemBatteryConsumer;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.List;
 
@@ -57,6 +62,7 @@
                     .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs)
                     .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah);
         }
+        // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper.
     }
 
     /**
@@ -73,6 +79,8 @@
             bs.usageTimeMs = durationMs;
             bs.sumPower();
             sippers.add(bs);
+
+            smearScreenBatterySipper(sippers, bs);
         }
     }
 
@@ -96,4 +104,60 @@
         }
         return power;
     }
+
+    /**
+     * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
+     * time, and store this in the {@link BatterySipper#screenPowerMah} field.
+     */
+    @VisibleForTesting
+    public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
+
+        long totalActivityTimeMs = 0;
+        final SparseLongArray activityTimeArray = new SparseLongArray();
+        for (int i = sippers.size() - 1; i >= 0; i--) {
+            final BatteryStats.Uid uid = sippers.get(i).uidObj;
+            if (uid != null) {
+                final long timeMs = getProcessForegroundTimeMs(uid);
+                activityTimeArray.put(uid.getUid(), timeMs);
+                totalActivityTimeMs += timeMs;
+            }
+        }
+
+        if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
+            final double screenPowerMah = screenSipper.totalPowerMah;
+            for (int i = sippers.size() - 1; i >= 0; i--) {
+                final BatterySipper sipper = sippers.get(i);
+                sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
+                        / totalActivityTimeMs;
+            }
+        }
+    }
+
+    /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */
+    @VisibleForTesting
+    public long getProcessForegroundTimeMs(BatteryStats.Uid uid) {
+        final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
+        final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP};
+
+        long timeUs = 0;
+        for (int type : foregroundTypes) {
+            final long localTime = uid.getProcessStateTime(type, rawRealTimeUs,
+                    BatteryStats.STATS_SINCE_CHARGED);
+            timeUs += localTime;
+        }
+
+        // Return the min value of STATE_TOP time and foreground activity time, since both of these
+        // time have some errors.
+        return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000;
+    }
+
+    /** Get the ForegroundActivity time of the given uid. */
+    @VisibleForTesting
+    public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
+        final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
+        if (timer == null) {
+            return 0;
+        }
+        return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
+    }
 }
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index fbbee94..fbad75e 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -22,8 +22,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Arrays;
 
 /**
  * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
@@ -31,9 +29,7 @@
  */
 public class SystemServerCpuThreadReader {
     private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
-    private int[] mBinderThreadNativeTids = new int[0];  // Sorted
 
-    private long[] mLastProcessCpuTimeUs;
     private long[] mLastThreadCpuTimesUs;
     private long[] mLastBinderThreadCpuTimesUs;
 
@@ -41,8 +37,6 @@
      * Times (in microseconds) spent by the system server UID.
      */
     public static class SystemServiceCpuThreadTimes {
-        // The entire process
-        public long[] processCpuTimesUs;
         // All threads
         public long[] threadCpuTimesUs;
         // Just the threads handling incoming binder calls
@@ -61,8 +55,10 @@
     }
 
     @VisibleForTesting
-    public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException {
-        this(new KernelSingleProcessCpuThreadReader(pid, procPath));
+    public SystemServerCpuThreadReader(int pid,
+            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader)
+            throws IOException {
+        this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader));
     }
 
     @VisibleForTesting
@@ -70,9 +66,15 @@
         mKernelCpuThreadReader = kernelCpuThreadReader;
     }
 
+    /**
+     * Start tracking CPU time-in-state for the process specified in the constructor.
+     */
+    public void startTrackingThreadCpuTime() {
+        mKernelCpuThreadReader.startTrackingThreadCpuTimes();
+    }
+
     public void setBinderThreadNativeTids(int[] nativeTids) {
-        mBinderThreadNativeTids = nativeTids.clone();
-        Arrays.sort(mBinderThreadNativeTids);
+        mKernelCpuThreadReader.setSelectedThreadIds(nativeTids);
     }
 
     /**
@@ -81,33 +83,27 @@
     @Nullable
     public SystemServiceCpuThreadTimes readDelta() {
         final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
-        if (mLastProcessCpuTimeUs == null) {
-            mLastProcessCpuTimeUs = new long[numCpuFrequencies];
+        if (mLastThreadCpuTimesUs == null) {
             mLastThreadCpuTimesUs = new long[numCpuFrequencies];
             mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
 
-            mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies];
             mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
             mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
         }
 
         final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids);
+                mKernelCpuThreadReader.getProcessCpuUsage();
         if (processCpuUsage == null) {
             return null;
         }
 
         for (int i = numCpuFrequencies - 1; i >= 0; i--) {
-            long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000;
             long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000;
             long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000;
-            mDeltaCpuThreadTimes.processCpuTimesUs[i] =
-                    Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]);
             mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
                     Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]);
             mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
                     Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]);
-            mLastProcessCpuTimeUs[i] = processCpuTimesUs;
             mLastThreadCpuTimesUs[i] = threadCpuTimesUs;
             mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs;
         }
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 4eaa016..9cc7243 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -70,7 +70,8 @@
                                           deviceInfo.isExternal(), deviceInfo.getSources(),
                                           deviceInfo.getKeyboardType(), kcmObj.get(),
                                           deviceInfo.hasVibrator(), hasMic,
-                                          deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor()));
+                                          deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor(),
+                                          deviceInfo.hasBattery()));
 
     const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     for (const InputDeviceInfo::MotionRange& range: ranges) {
@@ -90,9 +91,10 @@
     gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice");
     gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
 
-    gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
-                                                  "(IIILjava/lang/String;IILjava/lang/"
-                                                  "String;ZIILandroid/view/KeyCharacterMap;ZZZZ)V");
+    gInputDeviceClassInfo.ctor =
+            GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
+                             "(IIILjava/lang/String;IILjava/lang/"
+                             "String;ZIILandroid/view/KeyCharacterMap;ZZZZZ)V");
 
     gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
             "addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
index 52bed6b..dfae684 100644
--- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -26,239 +26,230 @@
 #include <android_runtime/Log.h>
 
 #include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
 
 namespace android {
 
+static constexpr uint16_t DEFAULT_THREAD_AGGREGATION_KEY = 0;
+static constexpr uint16_t SELECTED_THREAD_AGGREGATION_KEY = 1;
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
 // Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
 static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
 
-// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are
-// file names in the /proc/<pid>/task directory.
-static bool getThreadIds(const std::string &procPath, const pid_t pid,
-                         std::vector<pid_t> &outThreadIds) {
-    std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid);
+// Abstract class for readers of CPU time-in-state. There are two implementations of
+// this class: BpfCpuTimeInStateReader and MockCpuTimeInStateReader.  The former is used
+// by the production code. The latter is used by unit tests to provide mock
+// CPU time-in-state data via a Java implementation.
+class ICpuTimeInStateReader {
+public:
+    virtual ~ICpuTimeInStateReader() {}
 
-    struct dirent **dirlist;
-    int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL);
-    if (threadCount == -1) {
-        ALOGE("Cannot read directory %s", taskPath.c_str());
-        return false;
-    }
+    // Returns the overall number of cluser-frequency combinations
+    virtual size_t getCpuFrequencyCount();
 
-    outThreadIds.reserve(threadCount);
+    // Marks the CPU time-in-state tracking for threads of the specified TGID
+    virtual bool startTrackingProcessCpuTimes(pid_t) = 0;
 
-    for (int i = 0; i < threadCount; i++) {
-        pid_t tid;
-        if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) {
-            outThreadIds.push_back(tid);
+    // Marks the thread specified by its PID for CPU time-in-state tracking.
+    virtual bool startAggregatingTaskCpuTimes(pid_t, uint16_t) = 0;
+
+    // Retrieves the accumulated time-in-state data, which is organized as a map
+    // from aggregation keys to vectors of vectors using the format:
+    // { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+    //   aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+    // where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest
+    // freq.
+    virtual std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+    getAggregatedTaskCpuFreqTimes(pid_t, const std::vector<uint16_t> &);
+};
+
+// ICpuTimeInStateReader that uses eBPF to provide a map of aggregated CPU time-in-state values.
+// See cputtimeinstate.h/.cpp
+class BpfCpuTimeInStateReader : public ICpuTimeInStateReader {
+public:
+    size_t getCpuFrequencyCount() {
+        std::optional<std::vector<std::vector<uint32_t>>> cpuFreqs = android::bpf::getCpuFreqs();
+        if (!cpuFreqs) {
+            ALOGE("Cannot obtain CPU frequency count");
+            return 0;
         }
-        free(dirlist[i]);
-    }
-    free(dirlist);
 
-    return true;
+        size_t freqCount = 0;
+        for (auto cluster : *cpuFreqs) {
+            freqCount += cluster.size();
+        }
+
+        return freqCount;
+    }
+
+    bool startTrackingProcessCpuTimes(pid_t tgid) {
+        return android::bpf::startTrackingProcessCpuTimes(tgid);
+    }
+
+    bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+        return android::bpf::startAggregatingTaskCpuTimes(pid, aggregationKey);
+    }
+
+    std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+    getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
+        return android::bpf::getAggregatedTaskCpuFreqTimes(pid, aggregationKeys);
+    }
+};
+
+// ICpuTimeInStateReader that uses JNI to provide a map of aggregated CPU time-in-state
+// values.
+// This version of CpuTimeInStateReader is used exclusively for providing mock data in tests.
+class MockCpuTimeInStateReader : public ICpuTimeInStateReader {
+private:
+    JNIEnv *mEnv;
+    jobject mCpuTimeInStateReader;
+
+public:
+    MockCpuTimeInStateReader(JNIEnv *env, jobject cpuTimeInStateReader)
+          : mEnv(env), mCpuTimeInStateReader(cpuTimeInStateReader) {}
+
+    size_t getCpuFrequencyCount();
+
+    bool startTrackingProcessCpuTimes(pid_t tgid);
+
+    bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey);
+
+    std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+    getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys);
+};
+
+static ICpuTimeInStateReader *getCpuTimeInStateReader(JNIEnv *env,
+                                                      jobject cpuTimeInStateReaderObject) {
+    if (cpuTimeInStateReaderObject) {
+        return new MockCpuTimeInStateReader(env, cpuTimeInStateReaderObject);
+    } else {
+        return new BpfCpuTimeInStateReader();
+    }
 }
 
-// Reads contents of a time_in_state file and returns times as a vector of times per frequency
-// A time_in_state file contains pairs of frequency - time (in jiffies):
-//
-//    cpu0
-//    300000 30
-//    403200 0
-//    cpu4
-//    710400 10
-//    825600 20
-//    940800 30
-//
-static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid,
-                                 const size_t frequencyCount,
-                                 std::vector<uint64_t> &outThreadTimeInState) {
-    std::string timeInStateFilePath =
-            android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid);
-    std::string data;
+static jint getCpuFrequencyCount(JNIEnv *env, jclass, jobject cpuTimeInStateReaderObject) {
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
+    return cpuTimeInStateReader->getCpuFrequencyCount();
+}
 
-    if (!android::base::ReadFileToString(timeInStateFilePath, &data)) {
-        ALOGE("Cannot read file: %s", timeInStateFilePath.c_str());
-        return false;
-    }
+static jboolean startTrackingProcessCpuTimes(JNIEnv *env, jclass, jint tgid,
+                                             jobject cpuTimeInStateReaderObject) {
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
+    return cpuTimeInStateReader->startTrackingProcessCpuTimes(tgid);
+}
 
-    auto lines = android::base::Split(data, "\n");
-    size_t index = 0;
-    for (const auto &line : lines) {
-        if (line.empty()) {
-            continue;
-        }
+static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray selectedThreadIdArray,
+                                               jobject cpuTimeInStateReaderObject) {
+    ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
 
-        auto numbers = android::base::Split(line, " ");
-        if (numbers.size() != 2) {
-            continue;
-        }
-        uint64_t timeInState;
-        if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) {
-            ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str());
+    for (int i = 0; i < selectedThreadIds.size(); i++) {
+        if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i],
+                                                                SELECTED_THREAD_AGGREGATION_KEY)) {
             return false;
         }
-        if (index < frequencyCount) {
-            outThreadTimeInState[index] = timeInState;
-        }
-        index++;
     }
+    return true;
+}
 
+// Converts time-in-state data from a vector of vectors to a flat array.
+// Also converts from nanoseconds to milliseconds.
+static bool flattenTimeInStateData(ScopedLongArrayRW &cpuTimesMillis,
+                                   const std::vector<std::vector<uint64_t>> &data) {
+    size_t frequencyCount = cpuTimesMillis.size();
+    size_t index = 0;
+    for (const auto &cluster : data) {
+        for (const uint64_t &timeNanos : cluster) {
+            if (index < frequencyCount) {
+                cpuTimesMillis[index] = timeNanos / NSEC_PER_MSEC;
+            }
+            index++;
+        }
+    }
     if (index != frequencyCount) {
-        ALOGE("Incorrect number of frequencies %u in %s. Expected %u",
-              (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(),
-              (uint32_t)frequencyCount);
+        ALOGE("CPU time-in-state reader returned data for %zu frequencies; expected: %zu", index,
+              frequencyCount);
         return false;
     }
 
     return true;
 }
 
-static int pidCompare(const void *a, const void *b) {
-    return (*(pid_t *)a - *(pid_t *)b);
-}
-
-static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds,
-                                    const size_t selectedThreadCount) {
-    return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL;
-}
-
-// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency
+// Reads all CPU time-in-state data accumulated by BPF and aggregates per-frequency
 // time in state data for all threads.  Also, separately aggregates time in state for
 // selected threads whose TIDs are passes as selectedThreadIds.
-static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid,
-                                    const std::vector<pid_t> &threadIds,
-                                    const size_t frequencyCount, const pid_t *selectedThreadIds,
-                                    const size_t selectedThreadCount,
-                                    uint64_t *threadCpuTimesMillis,
-                                    uint64_t *selectedThreadCpuTimesMillis) {
-    for (size_t j = 0; j < frequencyCount; j++) {
-        threadCpuTimesMillis[j] = 0;
-        selectedThreadCpuTimesMillis[j] = 0;
-    }
-
-    for (size_t i = 0; i < threadIds.size(); i++) {
-        pid_t tid = threadIds[i];
-        std::vector<uint64_t> timeInState(frequencyCount);
-        if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) {
-            continue;
-        }
-
-        bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount);
-        for (size_t j = 0; j < frequencyCount; j++) {
-            threadCpuTimesMillis[j] += timeInState[j];
-            if (selectedThread) {
-                selectedThreadCpuTimesMillis[j] += timeInState[j];
-            }
-        }
-    }
-    for (size_t i = 0; i < frequencyCount; i++) {
-        threadCpuTimesMillis[i] *= gJiffyMillis;
-        selectedThreadCpuTimesMillis[i] *= gJiffyMillis;
-    }
-}
-
-// Reads process utime and stime from the /proc/<pid>/stat file.
-// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html.
-static bool getProcessCpuTime(const std::string &procPath, const pid_t pid,
-                              uint64_t &outTimeMillis) {
-    std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid);
-    std::string data;
-    if (!android::base::ReadFileToString(statFilePath, &data)) {
-        return false;
-    }
-
-    auto fields = android::base::Split(data, " ");
-    uint64_t utime, stime;
-
-    // Field 14 (counting from 1) is utime - process time in user space, in jiffies
-    // Field 15 (counting from 1) is stime - process time in system space, in jiffies
-    if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) ||
-        !android::base::ParseUint(fields[14], &stime)) {
-        ALOGE("Invalid file format %s", statFilePath.c_str());
-        return false;
-    }
-
-    outTimeMillis = (utime + stime) * gJiffyMillis;
-    return true;
-}
-
-// Estimates per cluster per frequency CPU time for the entire process
-// by distributing the total process CPU time proportionately to how much
-// CPU time its threads took on those clusters/frequencies.  This algorithm
-// works more accurately when when we have equally distributed concurrency.
-// TODO(b/169279846): obtain actual process CPU times from the kernel
-static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis,
-                                       const uint64_t *threadCpuTimesMillis,
-                                       const size_t frequencyCount,
-                                       uint64_t *processCpuTimesMillis) {
-    uint64_t totalCpuTimeAllThreads = 0;
-    for (size_t i = 0; i < frequencyCount; i++) {
-        totalCpuTimeAllThreads += threadCpuTimesMillis[i];
-    }
-
-    if (totalCpuTimeAllThreads != 0) {
-        for (size_t i = 0; i < frequencyCount; i++) {
-            processCpuTimesMillis[i] =
-                    processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads;
-        }
-    } else {
-        for (size_t i = 0; i < frequencyCount; i++) {
-            processCpuTimesMillis[i] = 0;
-        }
-    }
-}
-
-static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid,
-                                    jintArray selectedThreadIdArray,
-                                    jlongArray processCpuTimesMillisArray,
+static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jint pid,
                                     jlongArray threadCpuTimesMillisArray,
-                                    jlongArray selectedThreadCpuTimesMillisArray) {
-    ScopedUtfChars procPathChars(env, procPath);
-    ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
-    ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray);
+                                    jlongArray selectedThreadCpuTimesMillisArray,
+                                    jobject cpuTimeInStateReaderObject) {
     ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
     ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
 
-    std::string procPathStr(procPathChars.c_str());
-
-    // Get all thread IDs for the process.
-    std::vector<pid_t> threadIds;
-    if (!getThreadIds(procPathStr, pid, threadIds)) {
-        ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str());
-        return false;
-    }
-
-    size_t frequencyCount = processCpuTimesMillis.size();
+    const size_t frequencyCount = cpuTimeInStateReader->getCpuFrequencyCount();
 
     if (threadCpuTimesMillis.size() != frequencyCount) {
-        ALOGE("Invalid array length: threadCpuTimesMillis");
+        ALOGE("Invalid threadCpuTimesMillis array length: %zu frequencies; expected: %zu",
+              threadCpuTimesMillis.size(), frequencyCount);
         return false;
     }
+
     if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
-        ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray");
+        ALOGE("Invalid selectedThreadCpuTimesMillis array length: %zu frequencies; expected: %zu",
+              selectedThreadCpuTimesMillis.size(), frequencyCount);
         return false;
     }
 
-    aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(),
-                            selectedThreadIds.size(),
-                            reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
-                            reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get()));
-
-    uint64_t processCpuTime;
-    bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime);
-    if (ret) {
-        estimateProcessTimeInState(processCpuTime,
-                                   reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
-                                   frequencyCount,
-                                   reinterpret_cast<uint64_t *>(processCpuTimesMillis.get()));
+    for (size_t i = 0; i < frequencyCount; i++) {
+        threadCpuTimesMillis[i] = 0;
+        selectedThreadCpuTimesMillis[i] = 0;
     }
-    return ret;
+
+    std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> data =
+            cpuTimeInStateReader->getAggregatedTaskCpuFreqTimes(pid,
+                                                                {DEFAULT_THREAD_AGGREGATION_KEY,
+                                                                 SELECTED_THREAD_AGGREGATION_KEY});
+    if (!data) {
+        ALOGE("Cannot read thread CPU times for PID %d", pid);
+        return false;
+    }
+
+    if (!flattenTimeInStateData(threadCpuTimesMillis, (*data)[DEFAULT_THREAD_AGGREGATION_KEY])) {
+        return false;
+    }
+
+    if (!flattenTimeInStateData(selectedThreadCpuTimesMillis,
+                                (*data)[SELECTED_THREAD_AGGREGATION_KEY])) {
+        return false;
+    }
+
+    // threadCpuTimesMillis returns CPU times for _all_ threads, including the selected ones
+    for (size_t i = 0; i < frequencyCount; i++) {
+        threadCpuTimesMillis[i] += selectedThreadCpuTimesMillis[i];
+    }
+
+    return true;
 }
 
 static const JNINativeMethod g_single_methods[] = {
-        {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage},
+        {"getCpuFrequencyCount",
+         "(Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)I",
+         (void *)getCpuFrequencyCount},
+        {"startTrackingProcessCpuTimes",
+         "(ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+         (void *)startTrackingProcessCpuTimes},
+        {"startAggregatingThreadCpuTimes",
+         "([ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+         (void *)startAggregatingThreadCpuTimes},
+        {"readProcessCpuUsage",
+         "(I[J[J"
+         "Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+         (void *)readProcessCpuUsage},
 };
 
 int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
@@ -266,4 +257,77 @@
                                 g_single_methods, NELEM(g_single_methods));
 }
 
+size_t MockCpuTimeInStateReader::getCpuFrequencyCount() {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid = mEnv->GetMethodID(cls, "getCpuFrequencyCount", "()I");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method getCpuFrequencyCount");
+        return false;
+    }
+    return (size_t)mEnv->CallIntMethod(mCpuTimeInStateReader, mid);
+}
+
+bool MockCpuTimeInStateReader::startTrackingProcessCpuTimes(pid_t tgid) {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid = mEnv->GetMethodID(cls, "startTrackingProcessCpuTimes", "(I)Z");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method startTrackingProcessCpuTimes");
+        return false;
+    }
+    return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, tgid);
+}
+
+bool MockCpuTimeInStateReader::startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid = mEnv->GetMethodID(cls, "startAggregatingTaskCpuTimes", "(II)Z");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method startAggregatingTaskCpuTimes");
+        return false;
+    }
+    return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, pid, aggregationKey);
+}
+
+std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes(
+        pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid =
+            mEnv->GetMethodID(cls, "getAggregatedTaskCpuFreqTimes", "(I)[Ljava/lang/String;");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method getAggregatedTaskCpuFreqTimes");
+        return {};
+    }
+
+    std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map;
+
+    jobjectArray stringArray =
+            (jobjectArray)mEnv->CallObjectMethod(mCpuTimeInStateReader, mid, pid);
+    int size = mEnv->GetArrayLength(stringArray);
+    for (int i = 0; i < size; i++) {
+        ScopedUtfChars line(mEnv, (jstring)mEnv->GetObjectArrayElement(stringArray, i));
+        uint16_t aggregationKey;
+        std::vector<std::vector<uint64_t>> times;
+
+        // Each string is formatted like this: "aggKey:t0_0 t0_1...:t1_0 t1_1..."
+        auto fields = android::base::Split(line.c_str(), ":");
+        android::base::ParseUint(fields[0], &aggregationKey);
+
+        for (int j = 1; j < fields.size(); j++) {
+            auto numbers = android::base::Split(fields[j], " ");
+
+            std::vector<uint64_t> chunk;
+            for (int k = 0; k < numbers.size(); k++) {
+                uint64_t time;
+                android::base::ParseUint(numbers[k], &time);
+                chunk.emplace_back(time);
+            }
+            times.emplace_back(chunk);
+        }
+
+        map.emplace(aggregationKey, times);
+    }
+
+    return map;
+}
+
 } // namespace android
diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml
new file mode 100755
index 0000000..db438f2
--- /dev/null
+++ b/core/res/res/values-es-rMX/donottranslate-cldr.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_day_year">%-e %B %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%-e %b %Y, %H:%M:%S</string>
+    <string name="date_time">%1$s, %2$s</string>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 243c244..415a0a2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8288,6 +8288,23 @@
     </declare-styleable>
 
     <!-- =============================== -->
+    <!-- System Speech Recognition attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that
+         describes a {@link android.service.speech.RecognitionService}, which is referenced
+         from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data
+         entry.
+         @hide @SystemApi
+    -->
+    <declare-styleable name="OnDeviceRecognitionService">
+        <!-- Fully qualified class name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity" />
+    </declare-styleable>
+
+    <!-- =============================== -->
     <!-- Content Capture attributes -->
     <!-- =============================== -->
     <eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4e272f1..71b59e4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3800,6 +3800,12 @@
 -->
     <string name="config_defaultSearchUiService" translatable="false"></string>
 
+    <!-- The package name for the system's speech recognition service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         Example: "com.android.speech/.RecognitionService"
+    -->
+    <string name="config_defaultOnDeviceSpeechRecognitionService" translatable="false"></string>
+
     <string name="config_defaultMusicRecognitionService" translatable="false"></string>
 
     <!-- The package name for the default retail demo app.
@@ -4657,4 +4663,7 @@
     <bool name="config_telephony5gStandalone">false</bool>
     <!-- Whether the device enable the non-standalone (NSA) mode of 5G NR.-->
     <bool name="config_telephony5gNonStandalone">false</bool>
+
+    <!-- Whether to select voice/data/sms preference without user confirmation -->
+    <bool name="config_voice_data_sms_auto_fallback">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 98b36c5..f6d1b7d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1585,6 +1585,8 @@
 
     <!-- Template to be used to name enrolled fingerprints by default. -->
     <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
+    <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
+    <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
 
     <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
     <string-array name="fingerprint_error_vendor">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e92eb97..d7005e4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2487,6 +2487,7 @@
   <java-symbol type="string" name="fingerprint_error_lockout" />
   <java-symbol type="string" name="fingerprint_error_lockout_permanent" />
   <java-symbol type="string" name="fingerprint_name_template" />
+  <java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
   <java-symbol type="string" name="fingerprint_authenticated" />
   <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
   <java-symbol type="string" name="fingerprint_error_hw_not_present" />
@@ -3489,6 +3490,7 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="notification_channel_accessibility_magnification" />
   <java-symbol type="string" name="config_defaultAutofillService" />
+  <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
@@ -4165,4 +4167,6 @@
 
   <java-symbol type="bool" name="config_telephony5gStandalone" />
   <java-symbol type="bool" name="config_telephony5gNonStandalone" />
+
+  <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
 </resources>
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
index b319886..341ee37 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -149,7 +149,7 @@
                 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
                 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
                 false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
-                true /* hasSensor */);
+                true /* hasSensor */, false /* hasBattery */);
         assertTrue(d.hasSensor());
         return d;
     }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index fbe16f2..d2107ea 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -21,12 +21,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -194,7 +192,6 @@
         sippers.add(mBluetoothBatterySipper);
         sippers.add(mIdleBatterySipper);
         doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper);
-        doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any());
 
         final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers);
 
@@ -208,19 +205,20 @@
 
     @Test
     public void testSmearScreenBatterySipper() {
+        final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
         final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
-                BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */);
+                BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc);
         final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
-                BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */);
+                BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc);
         final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY,
-                BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */);
+                BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc);
 
         final List<BatterySipper> sippers = new ArrayList<>();
         sippers.add(sipperNull);
         sippers.add(sipperBg);
         sippers.add(sipperFg);
 
-        mBatteryStatsHelper.smearScreenBatterySipper(sippers, mScreenBatterySipper);
+        spc.smearScreenBatterySipper(sippers, mScreenBatterySipper);
 
         assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0);
         assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0);
@@ -249,13 +247,13 @@
 
     @Test
     public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() {
-        doReturn(TIME_STATE_FOREGROUND_US + 500).when(mBatteryStatsHelper)
+        final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
+        doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc)
                 .getForegroundActivityTotalTimeUs(eq(mUid), anyLong());
         doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP),
                 anyLong(), anyInt());
 
-        final long time = mBatteryStatsHelper.getProcessForegroundTimeMs(mUid,
-                BatteryStats.STATS_SINCE_CHARGED);
+        final long time = spc.getProcessForegroundTimeMs(mUid);
 
         assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
     }
@@ -291,15 +289,14 @@
     }
 
     private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
-            int uidCode, boolean isUidNull) {
+            int uidCode, boolean isUidNull, ScreenPowerCalculator spc) {
         final BatterySipper sipper = mock(BatterySipper.class);
         sipper.drainType = BatterySipper.DrainType.APP;
         sipper.totalPowerMah = totalPowerMah;
         doReturn(uidCode).when(sipper).getUid();
         if (!isUidNull) {
             final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
-            doReturn(activityTime).when(mBatteryStatsHelper).getProcessForegroundTimeMs(eq(uid),
-                    anyInt());
+            doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid));
             doReturn(uidCode).when(uid).getUid();
             sipper.uidObj = uid;
         }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index b5720a2..2de800b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -19,122 +19,87 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.os.FileUtils;
-
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelSingleProcessCpuThreadReaderTest {
 
-    private File mProcDirectory;
-
-    @Before
-    public void setUp() {
-        Context context = InstrumentationRegistry.getContext();
-        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        FileUtils.deleteContents(mProcDirectory);
-    }
-
     @Test
     public void getProcessCpuUsage() throws IOException {
-        setupDirectory(42,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                // Units are 10ms aka 10000Us
-                new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 600}},
-                new int[] {4500, 500});
+        // Units are nanoseconds
+        MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4, new String[] {
+                "0:1000000000 2000000000 3000000000:4000000000",
+                "1:100000000 200000000 300000000:400000000",
+        });
 
         KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42,
-                mProcDirectory.toPath());
+                mockReader);
+        reader.setSelectedThreadIds(new int[] {2, 3});
+        reader.startTrackingThreadCpuTimes();
         KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                reader.getProcessCpuUsage(new int[] {2, 3});
-        assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(new long[] {2000, 13000});
-        assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(new long[] {1000, 9000});
-        assertThat(processCpuUsage.processCpuTimesMillis).isEqualTo(new long[] {6666, 43333});
+                reader.getProcessCpuUsage();
+        assertThat(mockReader.mTrackedTgid).isEqualTo(42);
+        // The strings are formatted as <TID TGID AGG_KEY>, where AGG_KEY is 1 for binder
+        // threads and 0 for all other threads.
+        assertThat(mockReader.mTrackedTasks).containsExactly(
+                "2 1",
+                "3 1");
+        assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(
+                new long[] {1100, 2200, 3300, 4400});
+        assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(
+                new long[] {100, 200, 300, 400});
     }
 
     @Test
     public void getCpuFrequencyCount() throws IOException {
-        setupDirectory(13,
-                new int[] {13},
-                new int[] {1000, 2000, 3000},
-                new int[][] {{100, 200, 300}},
-                new int[] {14, 15});
+        MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(3, new String[0]);
 
         KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13,
-                mProcDirectory.toPath());
+                mockReader);
         int cpuFrequencyCount = reader.getCpuFrequencyCount();
         assertThat(cpuFrequencyCount).isEqualTo(3);
     }
 
-    private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies,
-            int[][] threadCpuTimes, int[] processCpuTimes)
-            throws IOException {
+    public static class MockCpuTimeInStateReader implements
+            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
+        private final int mCpuFrequencyCount;
+        private final String[] mAggregatedTaskCpuFreqTimes;
+        public int mTrackedTgid;
+        public List<String> mTrackedTasks = new ArrayList<>();
 
-        assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
-
-        try (OutputStream timeInStateStream =
-                     Files.newOutputStream(
-                             mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
-            for (int i = 0; i < cpuFrequencies.length; i++) {
-                final String line = cpuFrequencies[i] + " 0\n";
-                timeInStateStream.write(line.getBytes());
-            }
+        public MockCpuTimeInStateReader(int cpuFrequencyCount,
+                String[] aggregatedTaskCpuFreqTimes) {
+            mCpuFrequencyCount = cpuFrequencyCount;
+            mAggregatedTaskCpuFreqTimes = aggregatedTaskCpuFreqTimes;
         }
 
-        Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
-
-        // Make /proc/$PID
-        assertTrue(processPath.toFile().mkdirs());
-
-        // Write /proc/$PID/stat. Only the fields 14-17 matter.
-        try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
-            timeInStateStream.write(
-                    (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
-                            + processCpuTimes[0] + " "
-                            + processCpuTimes[1] + " "
-                            + "16 17 18 19 20 ...").getBytes());
+        @Override
+        public int getCpuFrequencyCount() {
+            return mCpuFrequencyCount;
         }
 
-        // Make /proc/$PID/task
-        final Path selfThreadsPath = processPath.resolve("task");
-        assertTrue(selfThreadsPath.toFile().mkdirs());
+        @Override
+        public boolean startTrackingProcessCpuTimes(int tgid) {
+            mTrackedTgid = tgid;
+            return true;
+        }
 
-        // Make thread directories
-        for (int i = 0; i < threadIds.length; i++) {
-            // Make /proc/$PID/task/$TID
-            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
-            assertTrue(threadPath.toFile().mkdirs());
+        public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
+            mTrackedTasks.add(pid + " " + aggregationKey);
+            return true;
+        }
 
-            // Make /proc/$PID/task/$TID/time_in_state
-            try (OutputStream timeInStateStream =
-                         Files.newOutputStream(threadPath.resolve("time_in_state"))) {
-                for (int j = 0; j < cpuFrequencies.length; j++) {
-                    final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n";
-                    timeInStateStream.write(line.getBytes());
-                }
-            }
+        public String[] getAggregatedTaskCpuFreqTimes(int pid) {
+            return mAggregatedTaskCpuFreqTimes;
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
index 121c637..d116d4d 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -16,146 +16,86 @@
 
 package com.android.internal.os;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
-import android.content.Context;
-import android.os.FileUtils;
-
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SystemServerCpuThreadReaderTest {
-    private File mProcDirectory;
-
-    @Before
-    public void setUp() {
-        Context context = InstrumentationRegistry.getContext();
-        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        FileUtils.deleteContents(mProcDirectory);
-    }
 
     @Test
-    public void testReaderDelta_firstTime() throws IOException {
+    public void testReadDelta() throws IOException {
         int pid = 42;
-        setupDirectory(
-                pid,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                // Units are 10ms aka 10000Us
-                new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
-                new int[] {1400, 1500});
 
-        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
-                mProcDirectory.toPath(), pid);
-        reader.setBinderThreadNativeTids(new int[] {1, 3});
-        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
-                reader.readDelta();
-        assertArrayEquals(new long[] {100 * 10000, 1100 * 10000},
-                systemServiceCpuThreadTimes.threadCpuTimesUs);
-        assertArrayEquals(new long[] {0, 600 * 10000},
-                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
-    }
+        MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4);
+        // Units are nanoseconds
+        mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
+                "0:1000000000 2000000000 3000000000:4000000000",
+                "1:100000000 200000000 300000000:400000000",
+        });
 
-    @Test
-    public void testReaderDelta_nextTime() throws IOException {
-        int pid = 42;
-        setupDirectory(
-                pid,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
-                new int[] {1400, 1500});
-
-        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
-                mProcDirectory.toPath(), pid);
+        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(pid, mockReader);
         reader.setBinderThreadNativeTids(new int[] {1, 3});
 
-        // First time, populate "last" snapshot
-        reader.readDelta();
-
-        FileUtils.deleteContents(mProcDirectory);
-        setupDirectory(
-                pid,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}},
-                new int[] {2400, 2500});
-
-        // Second time, get the actual delta
+        // The first invocation of readDelta populates the "last" snapshot
         SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
                 reader.readDelta();
 
-        assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000},
-                systemServiceCpuThreadTimes.threadCpuTimesUs);
-        assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000},
-                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+        assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
+                .isEqualTo(new long[] {1100000, 2200000, 3300000, 4400000});
+        assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
+                .isEqualTo(new long[] {100000, 200000, 300000, 400000});
+
+        mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
+                "0:1010000000 2020000000 3030000000:4040000000",
+                "1:101000000 202000000 303000000:404000000",
+        });
+
+        // The second invocation gets the actual delta
+        systemServiceCpuThreadTimes = reader.readDelta();
+
+        assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
+                .isEqualTo(new long[] {11000, 22000, 33000, 44000});
+        assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
+                .isEqualTo(new long[] {1000, 2000, 3000, 4000});
     }
 
-    private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes,
-            int[] processCpuTimes)
-            throws IOException {
+    public static class MockCpuTimeInStateReader implements
+            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
+        private final int mCpuFrequencyCount;
+        private String[] mAggregatedTaskCpuFreqTimes;
 
-        assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
-
-        try (OutputStream timeInStateStream =
-                     Files.newOutputStream(
-                             mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
-            for (int i = 0; i < cpuFrequencies.length; i++) {
-                final String line = cpuFrequencies[i] + " 0\n";
-                timeInStateStream.write(line.getBytes());
-            }
+        MockCpuTimeInStateReader(int frequencyCount) {
+            mCpuFrequencyCount = frequencyCount;
         }
 
-        Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
-        // Make /proc/$PID
-        assertTrue(processPath.toFile().mkdirs());
-
-        // Write /proc/$PID/stat. Only the fields 14-17 matter.
-        try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
-            timeInStateStream.write(
-                    (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
-                            + processCpuTimes[0] + " "
-                            + processCpuTimes[1] + " "
-                            + "16 17 18 19 20 ...").getBytes());
+        @Override
+        public int getCpuFrequencyCount() {
+            return mCpuFrequencyCount;
         }
 
-        // Make /proc/$PID/task
-        final Path selfThreadsPath = processPath.resolve("task");
-        assertTrue(selfThreadsPath.toFile().mkdirs());
+        @Override
+        public boolean startTrackingProcessCpuTimes(int tgid) {
+            return true;
+        }
 
-        // Make thread directories
-        for (int i = 0; i < threadIds.length; i++) {
-            // Make /proc/$PID/task/$TID
-            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
-            assertTrue(threadPath.toFile().mkdirs());
+        public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
+            return true;
+        }
 
-            // Make /proc/$PID/task/$TID/time_in_state
-            try (OutputStream timeInStateStream =
-                         Files.newOutputStream(threadPath.resolve("time_in_state"))) {
-                for (int j = 0; j < cpuFrequencies.length; j++) {
-                    final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
-                    timeInStateStream.write(line.getBytes());
-                }
-            }
+        public void setAggregatedTaskCpuFreqTimes(String[] mAggregatedTaskCpuFreqTimes) {
+            this.mAggregatedTaskCpuFreqTimes = mAggregatedTaskCpuFreqTimes;
+        }
+
+        public String[] getAggregatedTaskCpuFreqTimes(int pid) {
+            return mAggregatedTaskCpuFreqTimes;
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index a5cafb9..24741fe 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -62,7 +62,6 @@
     public void testCalculateApp() {
         // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
         mMockSystemServerCpuThreadReader.setCpuTimes(
-                new long[] {10000, 15000, 20000, 25000, 30000, 35000, 40000},
                 new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
                 new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
 
@@ -144,9 +143,7 @@
             super(null);
         }
 
-        public void setCpuTimes(long[] processCpuTimesUs, long[] threadCpuTimesUs,
-                long[] binderThreadCpuTimesUs) {
-            mThreadTimes.processCpuTimesUs = processCpuTimesUs;
+        public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
             mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
             mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
         }
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index 496e470..ad4c3fe 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -190,7 +190,7 @@
     }
 
     /**
-     * Create a filter that applies the color filter to the provided RenderEffect
+     * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect
      *
      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
      * @param renderEffect Source to be transformed by the specified {@link ColorFilter}
@@ -209,7 +209,7 @@
     }
 
     /**
-     * Create a filter that applies the color filter to the contents of the
+     * Create a {@link RenderEffect} that applies the color filter to the contents of the
      * {@link android.graphics.RenderNode} that this RenderEffect is installed on
      * @param colorFilter ColorFilter applied to the content in the input RenderEffect
      */
@@ -224,7 +224,7 @@
     }
 
     /**
-     * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
+     * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
      * combined by the specified {@link BlendMode}
      *
      * @param dst The Dst pixels used in blending
@@ -248,8 +248,8 @@
     }
 
     /**
-     * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
-     * treated as the source bitmap passed to 'outer', i.e.
+     * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of
+     * 'inner' are treated as the source bitmap passed to 'outer', i.e.
      *
      * <pre>
      * {@code
@@ -278,6 +278,18 @@
             );
     }
 
+    /**
+     * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}.
+     * This is useful to create an input for other {@link RenderEffect} types such as
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)}
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
+     * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}.
+     */
+    @NonNull
+    public static RenderEffect createShaderEffect(@NonNull Shader shader) {
+        return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
+    }
+
     private final long mNativeRenderEffect;
 
     /* only constructed from static factory methods */
@@ -305,5 +317,6 @@
     private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
     private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
     private static native long nativeCreateChainEffect(long outer, long inner);
+    private static native long nativeCreateShaderEffect(long shader);
     private static native long nativeGetFinalizer();
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index eaa704f..367f7e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -262,7 +262,6 @@
             final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
             mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
             mTimeoutHandler.resetTimer();
-
             mOneHandedUiEventLogger.writeEvent(
                     OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
         }
@@ -273,6 +272,9 @@
         if (mDisplayAreaOrganizer.isInOneHanded()) {
             mDisplayAreaOrganizer.scheduleOffset(0, 0);
             mTimeoutHandler.removeTimer();
+            //  Log metrics for Gesture navigation mode.
+            mOneHandedUiEventLogger.writeEvent(
+                    OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
         }
     }
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 615bf4d..58cbea6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -615,6 +615,7 @@
         "tests/unit/CommonPoolTests.cpp",
         "tests/unit/DamageAccumulatorTests.cpp",
         "tests/unit/DeferredLayerUpdaterTests.cpp",
+        "tests/unit/EglManagerTests.cpp",
         "tests/unit/FatVectorTests.cpp",
         "tests/unit/GraphicsStatsServiceTests.cpp",
         "tests/unit/LayerUpdateQueueTests.cpp",
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 3aa5b4b..ca5f853 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -17,8 +17,10 @@
 #pragma once
 
 #include "pipeline/skia/SkiaDisplayList.h"
+#include "canvas/CanvasOpBuffer.h"
 
 #include <memory>
+#include <variant>
 
 namespace android {
 namespace uirenderer {
@@ -28,29 +30,25 @@
 };
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
-/**
- * Data structure that holds the list of commands used in display list stream
- */
-//using DisplayList = skiapipeline::SkiaDisplayList;
-class DisplayList {
+class SkiaDisplayListWrapper {
 public:
     // Constructs an empty (invalid) DisplayList
-    explicit DisplayList() {}
+    explicit SkiaDisplayListWrapper() {}
 
     // Constructs a DisplayList from a SkiaDisplayList
-    explicit DisplayList(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
+    explicit SkiaDisplayListWrapper(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
         : mImpl(std::move(impl)) {}
 
     // Move support
-    DisplayList(DisplayList&& other) : mImpl(std::move(other.mImpl)) {}
-    DisplayList& operator=(DisplayList&& other) {
+    SkiaDisplayListWrapper(SkiaDisplayListWrapper&& other) : mImpl(std::move(other.mImpl)) {}
+    SkiaDisplayListWrapper& operator=(SkiaDisplayListWrapper&& other) {
         mImpl = std::move(other.mImpl);
         return *this;
     }
 
     // No copy support
-    DisplayList(const DisplayList& other) = delete;
-    DisplayList& operator=(const DisplayList&) = delete;
+    SkiaDisplayListWrapper(const SkiaDisplayListWrapper& other) = delete;
+    SkiaDisplayListWrapper& operator=(const SkiaDisplayListWrapper&) = delete;
 
     void updateChildren(std::function<void(RenderNode*)> updateFn) {
         mImpl->updateChildren(std::move(updateFn));
@@ -137,7 +135,7 @@
 
     void applyColorTransform(ColorTransform transform) {
         if (mImpl) {
-            mImpl->mDisplayList.applyColorTransform(transform);
+            mImpl->applyColorTransform(transform);
         }
     }
 
@@ -145,5 +143,172 @@
     std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl;
 };
 
+
+/**
+ * Data structure that holds the list of commands used in display list stream
+ */
+//using DisplayList = skiapipeline::SkiaDisplayList;
+class MultiDisplayList {
+private:
+    using SkiaDisplayList = skiapipeline::SkiaDisplayList;
+
+    struct EmptyList {
+        bool hasText() const { return false; }
+        void updateChildren(std::function<void(RenderNode*)> updateFn) {}
+        bool isEmpty() const { return true; }
+        bool containsProjectionReceiver() const { return false; }
+        bool hasVectorDrawables() const { return false; }
+        size_t getUsedSize() const { return 0; }
+        size_t getAllocatedSize() const { return 0; }
+        void output(std::ostream& output, uint32_t level) const { }
+        bool hasFunctor() const { return false; }
+        bool prepareListAndChildren(
+                TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+                std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+            return false;
+        }
+        void syncContents(const WebViewSyncData& data) { }
+        void applyColorTransform(ColorTransform transform) { }
+    };
+
+    std::variant<EmptyList, std::unique_ptr<SkiaDisplayList>, CanvasOpBuffer> mImpls;
+
+    template <typename T>
+    static constexpr T& get(T& t) { return t; }
+    template <typename T>
+    static constexpr const T& get(const T& t) { return t; }
+
+    template <typename T>
+    static constexpr T& get(std::unique_ptr<T>& t) { return *t; }
+    template <typename T>
+    static constexpr const T& get(const std::unique_ptr<T>& t) { return *t; }
+
+    template <typename T>
+    auto apply(T&& t) {
+        return std::visit([&t](auto& it) -> auto {
+            return t(get(it));
+        }, mImpls);
+    }
+
+    template <typename T>
+    auto apply(T&& t) const {
+        return std::visit([&t](const auto& it) -> auto {
+            return t(get(it));
+        }, mImpls);
+    }
+
+public:
+    // Constructs an empty (invalid) DisplayList
+    explicit MultiDisplayList() {}
+
+    // Constructs a DisplayList from a SkiaDisplayList
+    explicit MultiDisplayList(std::unique_ptr<SkiaDisplayList> impl)
+        : mImpls(std::move(impl)) {}
+
+    explicit MultiDisplayList(CanvasOpBuffer&& opBuffer) : mImpls(std::move(opBuffer)) {}
+
+    // Move support
+    MultiDisplayList(MultiDisplayList&& other) : mImpls(std::move(other.mImpls)) {}
+    MultiDisplayList& operator=(MultiDisplayList&& other) {
+        mImpls = std::move(other.mImpls);
+        return *this;
+    }
+
+    // No copy support
+    MultiDisplayList(const MultiDisplayList& other) = delete;
+    MultiDisplayList& operator=(const MultiDisplayList&) = delete;
+
+    void updateChildren(std::function<void(RenderNode*)> updateFn) {
+        apply([&](auto& it) { it.updateChildren(std::move(updateFn)); });
+    }
+
+    [[nodiscard]] explicit operator bool() const {
+        return isValid();
+    }
+
+    // If true this DisplayList contains a backing content, even if that content is empty
+    // If false, there this DisplayList is in an "empty" state
+    [[nodiscard]] bool isValid() const {
+        return mImpls.index() != 0;
+    }
+
+    [[nodiscard]] bool isEmpty() const {
+        return apply([](const auto& it) -> auto { return it.isEmpty(); });
+    }
+
+    [[nodiscard]] bool hasContent() const {
+        return !isEmpty();
+    }
+
+    [[nodiscard]] bool containsProjectionReceiver() const {
+        return apply([](const auto& it) -> auto { return it.containsProjectionReceiver(); });
+    }
+
+    [[nodiscard]] SkiaDisplayList* asSkiaDl() {
+        return std::get<1>(mImpls).get();
+    }
+
+    [[nodiscard]] const SkiaDisplayList* asSkiaDl() const {
+        return std::get<1>(mImpls).get();
+    }
+
+    [[nodiscard]] bool hasVectorDrawables() const {
+        return apply([](const auto& it) -> auto { return it.hasVectorDrawables(); });
+    }
+
+    void clear(RenderNode* owningNode = nullptr) {
+        if (owningNode && mImpls.index() == 1) {
+            auto& skiaDl = std::get<1>(mImpls);
+            if (skiaDl->reuseDisplayList(owningNode)) {
+                skiaDl.release();
+            }
+        }
+        mImpls = EmptyList{};
+    }
+
+    [[nodiscard]] size_t getUsedSize() const {
+        return apply([](const auto& it) -> auto { return it.getUsedSize(); });
+    }
+
+    [[nodiscard]] size_t getAllocatedSize() const {
+        return apply([](const auto& it) -> auto { return it.getAllocatedSize(); });
+    }
+
+    void output(std::ostream& output, uint32_t level) const {
+        apply([&](const auto& it) { it.output(output, level); });
+    }
+
+    [[nodiscard]] bool hasFunctor() const {
+        return apply([](const auto& it) -> auto { return it.hasFunctor(); });
+    }
+
+    bool prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+        return apply([&](auto& it) -> auto {
+            return it.prepareListAndChildren(observer, info, functorsNeedLayer, std::move(childFn));
+        });
+    }
+
+    void syncContents(const WebViewSyncData& data) {
+        apply([&](auto& it) { it.syncContents(data); });
+    }
+
+    [[nodiscard]] bool hasText() const {
+        return apply([](const auto& it) -> auto { return it.hasText(); });
+    }
+
+    void applyColorTransform(ColorTransform transform) {
+        apply([=](auto& it) { it.applyColorTransform(transform); });
+    }
+
+    [[nodiscard]] CanvasOpBuffer& asOpBuffer() {
+        return std::get<CanvasOpBuffer>(mImpls);
+    }
+};
+
+// For now stick to the original single-type container to avoid any regressions
+using DisplayList = SkiaDisplayListWrapper;
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
index d749d2f..f9a6101 100644
--- a/libs/hwui/canvas/CanvasFrontend.h
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -147,8 +147,7 @@
 public:
     template<class... Args>
     CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
-            mReceiver(std::forward<Args>(args)...) { }
-    ~CanvasFrontend() = default;
+            mReceiver(std::in_place, std::forward<Args>(args)...) { }
 
     void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
         if (internalSave(flagsToSaveEntry(flags))) {
@@ -186,7 +185,10 @@
         submit(std::move(op));
     }
 
-    const CanvasOpReceiver& receiver() const { return *mReceiver; }
+    const CanvasOpReceiver& receiver() const {
+        LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
+        return *mReceiver;
+    }
 
     CanvasOpReceiver finish() {
         auto ret = std::move(mReceiver.value());
@@ -205,6 +207,7 @@
 
     template <CanvasOpType T>
     void submit(CanvasOp<T>&& op) {
+        LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
         mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
     }
 };
diff --git a/libs/hwui/canvas/CanvasOpBuffer.cpp b/libs/hwui/canvas/CanvasOpBuffer.cpp
index 7054e47e..6089c572 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.cpp
+++ b/libs/hwui/canvas/CanvasOpBuffer.cpp
@@ -22,4 +22,32 @@
 
 template class OpBuffer<CanvasOpType, CanvasOpContainer>;
 
+void CanvasOpBuffer::updateChildren(std::function<void(RenderNode*)> updateFn) {
+    // TODO: Do we need a fast-path for finding children?
+    if (mHas.children) {
+        for (auto& iter : filter<CanvasOpType::DrawRenderNode>()) {
+            updateFn(iter->renderNode.get());
+        }
+    }
+}
+
+void CanvasOpBuffer::output(std::ostream& output, uint32_t level) const {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
+bool CanvasOpBuffer::prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+    LOG_ALWAYS_FATAL("TODO");
+    return false;
+}
+
+void CanvasOpBuffer::syncContents(const WebViewSyncData& data) {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
+void CanvasOpBuffer::applyColorTransform(ColorTransform transform) {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
 }  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h
index 07e079a..af797ca 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.h
+++ b/libs/hwui/canvas/CanvasOpBuffer.h
@@ -19,10 +19,17 @@
 #include <SkMatrix.h>
 
 #include "CanvasOpTypes.h"
+#include "CanvasTransform.h"
 #include "OpBuffer.h"
+#include "TreeInfo.h"
+#include "private/hwui/WebViewFunctor.h"
+
+#include <functional>
 
 namespace android::uirenderer {
 
+class RenderNode;
+
 template <CanvasOpType T>
 struct CanvasOp;
 
@@ -53,12 +60,74 @@
 };
 
 extern template class OpBuffer<CanvasOpType, CanvasOpContainer>;
-class CanvasOpBuffer final : public OpBuffer<CanvasOpType, CanvasOpContainer> {
+class CanvasOpBuffer final : private OpBuffer<CanvasOpType, CanvasOpContainer> {
+private:
+    using SUPER = OpBuffer<CanvasOpType, CanvasOpContainer>;
+
 public:
+    // Expose select superclass methods publicly
+    using SUPER::for_each;
+    using SUPER::size;
+    using SUPER::resize;
+
     template <CanvasOpType T>
     void push(CanvasOp<T>&& op) {
         push_container(CanvasOpContainer<T>(std::move(op)));
     }
+
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        if constexpr (IsDrawOp(T)) {
+            mHas.content = true;
+        }
+        if constexpr (T == CanvasOpType::DrawRenderNode) {
+            mHas.children = true;
+            // use staging property, since recording on UI thread
+            if (op->renderNode->stagingProperties().isProjectionReceiver()) {
+                mHas.projectionReceiver = true;
+            }
+        }
+        SUPER::push_container(std::move(op));
+    }
+
+    void clear() {
+        mHas = Contains{};
+        SUPER::clear();
+    }
+
+    void updateChildren(std::function<void(RenderNode*)> updateFn);
+    bool prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn);
+    void syncContents(const WebViewSyncData& data);
+    void applyColorTransform(ColorTransform transform);
+
+    [[nodiscard]] bool isEmpty() const { return !mHas.content; }
+    [[nodiscard]] bool hasText() const { return mHas.text; }
+    [[nodiscard]] bool hasVectorDrawables() const { return mHas.vectorDrawable; }
+    [[nodiscard]] bool containsProjectionReceiver() const { return mHas.projectionReceiver; }
+    [[nodiscard]] bool hasFunctor() const { return mHas.functor; }
+
+    [[nodiscard]] size_t getUsedSize() const {
+        return size();
+    }
+
+    [[nodiscard]] size_t getAllocatedSize() const {
+        return capacity();
+    }
+
+    void output(std::ostream& output, uint32_t level) const;
+
+private:
+    struct Contains {
+        bool content : 1 = false;
+        bool children : 1 = false;
+        bool projectionReceiver : 1 = false;
+        bool text : 1 = false;
+        bool vectorDrawable : 1 = false;
+        bool functor : 1 = false;
+    };
+    Contains mHas;
 };
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp
index 0093c38..9297604 100644
--- a/libs/hwui/canvas/CanvasOpRasterizer.cpp
+++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp
@@ -33,21 +33,15 @@
     SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I());
 
     source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) {
-        if constexpr (
-            T == CanvasOpType::BeginZ ||
-            T == CanvasOpType::EndZ   ||
-            T == CanvasOpType::DrawLayer
-        ) {
-            // Do beginZ or endZ
-            LOG_ALWAYS_FATAL("TODO");
-            return;
-        } else {
+        if constexpr (CanvasOpTraits::can_draw<CanvasOp<T>>) {
             // Generic OP
             // First apply the current transformation
             destination->setMatrix(SkMatrix::Concat(currentGlobalTransform, op->transform()));
             // Now draw it
             (*op)->draw(destination);
+            return;
         }
+        LOG_ALWAYS_FATAL("TODO, unable to rasterize %d", static_cast<int>(T));
     });
 }
 
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index cde50bd..b55ef9d 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -35,7 +35,8 @@
     ClipPath,
 
     // Drawing ops
-    DrawColor,
+    DRAW_OP_BEGIN,
+    DrawColor = DRAW_OP_BEGIN,
     DrawRect,
     DrawRegion,
     DrawRoundRect,
@@ -59,10 +60,16 @@
     DrawImageLattice,
     DrawPicture,
     DrawLayer,
+    DrawRenderNode,
+    DRAW_OP_END = DrawRenderNode,
 
     // TODO: Rest
 
     COUNT  // must be last
 };
 
+static constexpr bool IsDrawOp(CanvasOpType t) {
+    return CanvasOpType::DRAW_OP_BEGIN <= t && t <= CanvasOpType::DRAW_OP_END;
+}
+
 }  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index cceba59..77e59ac 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -24,13 +24,15 @@
 #include <SkImage.h>
 #include <SkPicture.h>
 #include <SkRuntimeEffect.h>
-#include <hwui/Bitmap.h>
-#include <log/log.h>
-#include "CanvasProperty.h"
-#include "Points.h"
 
+#include <log/log.h>
+
+#include "hwui/Bitmap.h"
+#include "CanvasProperty.h"
 #include "CanvasOpTypes.h"
 #include "Layer.h"
+#include "Points.h"
+#include "RenderNode.h"
 
 #include <experimental/type_traits>
 #include <utility>
@@ -443,6 +445,11 @@
     sp<Layer> layer;
 };
 
+template<>
+struct CanvasOp<CanvasOpType::DrawRenderNode> {
+    sp<RenderNode> renderNode;
+};
+
 // cleanup our macros
 #undef ASSERT_DRAWABLE
 
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 1237d69..8b5cdbb 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -64,7 +64,7 @@
     static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
     using ItemHeader = OpBufferItemHeader<ItemTypes>;
 
-    OpBuffer() = default;
+    explicit OpBuffer() = default;
 
     // Prevent copying by default
     OpBuffer(const OpBuffer&) = delete;
@@ -135,7 +135,7 @@
 
     template <typename F>
     void for_each(F&& f) const {
-        for_each(std::forward<F>(f), ItemTypesSequence{});
+        do_for_each(std::forward<F>(f), ItemTypesSequence{});
     }
 
     void clear();
@@ -225,7 +225,7 @@
     }
 
     template <typename F, std::size_t... I>
-    void for_each(F&& f, std::index_sequence<I...>) const {
+    void do_for_each(F&& f, std::index_sequence<I...>) const {
         // Validate we're not empty
         if (isEmpty()) return;
 
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 97c40d6..fa1752c 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -115,6 +115,18 @@
     return reinterpret_cast<jlong>(composeFilter.release());
 }
 
+static jlong createShaderEffect(
+    JNIEnv* env,
+    jobject,
+    jlong shaderHandle
+) {
+    auto* shader = reinterpret_cast<const SkShader*>(shaderHandle);
+    sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader(
+          sk_ref_sp(shader), nullptr
+    );
+    return reinterpret_cast<jlong>(shaderFilter.release());
+}
+
 static void RenderEffect_safeUnref(SkImageFilter* filter) {
     SkSafeUnref(filter);
 }
@@ -130,7 +142,8 @@
     {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
     {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
     {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
-    {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}
+    {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+    {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
 };
 
 int register_android_graphics_RenderEffect(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 483264f..1136e58 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <deque>
+
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
@@ -23,8 +25,6 @@
 #include "utils/LinearAllocator.h"
 #include "utils/Pair.h"
 
-#include <deque>
-
 namespace android {
 namespace uirenderer {
 
@@ -46,8 +46,10 @@
 
 class SkiaDisplayList {
 public:
-    size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
-    size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); }
+    size_t getUsedSize() const { return allocator.usedSize() + mDisplayList.usedSize(); }
+    size_t getAllocatedSize() const {
+        return allocator.allocatedSize() + mDisplayList.allocatedSize();
+    }
 
     ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
@@ -109,6 +111,10 @@
      */
     void syncContents(const WebViewSyncData& data);
 
+    void applyColorTransform(ColorTransform transform) {
+        mDisplayList.applyColorTransform(transform);
+    }
+
     /**
      * ONLY to be called by RenderNode::prepareTree in order to prepare this
      * list while the UI thread is blocked.  Here we can upload mutable bitmaps
@@ -154,12 +160,12 @@
     std::deque<RenderNodeDrawable> mChildNodes;
     std::deque<FunctorDrawable*> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
+
 private:
     std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
+
 public:
-    void appendVD(VectorDrawableRoot* r) {
-        appendVD(r, SkMatrix::I());
-    }
+    void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); }
 
     void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) {
         mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat));
diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
index 05b1179..4ddcf6f 100644
--- a/libs/hwui/tests/unit/CanvasFrontendTests.cpp
+++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
@@ -124,12 +124,12 @@
 
 TEST(CanvasFrontend, drawOpTransform) {
     CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100);
-    const auto& receiver = opCanvas.receiver();
+    const auto &receiver = opCanvas.receiver();
 
     auto makeDrawRect = [] {
         return CanvasOp<CanvasOpType::DrawRect>{
-            .rect = SkRect::MakeWH(50, 50),
-            .paint = SkPaint(SkColors::kBlack),
+                .rect = SkRect::MakeWH(50, 50),
+                .paint = SkPaint(SkColors::kBlack),
         };
     };
 
@@ -167,14 +167,14 @@
 
     {
         // First result should be identity
-        const auto& result = transforms[0];
+        const auto &result = transforms[0];
         EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType());
         EXPECT_EQ(SkMatrix::I(), result);
     }
 
     {
         // Should be translate 10, 10
-        const auto& result = transforms[1];
+        const auto &result = transforms[1];
         EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(10, 10);
@@ -183,7 +183,7 @@
 
     {
         // Should be translate 10, 10 + scale 2, 4
-        const auto& result = transforms[2];
+        const auto &result = transforms[2];
         EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(10, 10);
@@ -193,7 +193,7 @@
 
     {
         // Should be translate 10, 10 + translate 20, 15
-        const auto& result = transforms[3];
+        const auto &result = transforms[3];
         EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(30, 25);
@@ -202,9 +202,9 @@
 
     {
         // Should be translate 10, 10 + translate 20, 15 + rotate 90
-        const auto& result = transforms[4];
+        const auto &result = transforms[4];
         EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask,
-                result.getType());
+                  result.getType());
         SkMatrix m;
         m.setTranslate(30, 25);
         m.preRotate(90.f);
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index c9e8d80..12fa32b 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -149,7 +149,7 @@
     CanvasOpBuffer buffer;
     EXPECT_EQ(buffer.size(), 0);
     size_t numPts = 3;
-    auto pts = sk_ref_sp(
+    auto pts = sk_sp<Points>(
           new Points({
               {32, 16},
               {48, 48},
@@ -192,7 +192,7 @@
     CanvasOpBuffer buffer;
     EXPECT_EQ(buffer.size(), 0);
     size_t numPts = 3;
-    auto pts = sk_ref_sp(
+    auto pts = sk_sp<Points>(
         new Points({
                {32, 16},
                {48, 48},
diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp
new file mode 100644
index 0000000..f7f2406
--- /dev/null
+++ b/libs/hwui/tests/unit/EglManagerTests.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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 <gtest/gtest.h>
+
+#include "renderthread/EglManager.h"
+#include "tests/common/TestContext.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+TEST(EglManager, doesSurfaceLeak) {
+    EglManager eglManager;
+    eglManager.initialize();
+
+    ASSERT_TRUE(eglManager.hasEglContext());
+
+    auto colorSpace = SkColorSpace::MakeSRGB();
+    for (int i = 0; i < 100; i++) {
+        TestContext context;
+        auto result =
+                eglManager.createSurface(context.surface().get(), ColorMode::Default, colorSpace);
+        EXPECT_TRUE(result);
+        EGLSurface surface = result.unwrap();
+        eglManager.destroySurface(surface);
+    }
+
+    eglManager.destroy();
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6dd57b1..8c999c4 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -404,6 +404,7 @@
     EXPECT_TRUE(pipeline->isSurfaceReady());
     renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
+    LOG_ALWAYS_FATAL_IF(pipeline->isSurfaceReady());
 }
 
 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d40de76..205c1f4 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -354,7 +354,7 @@
 
     /**
      * @hide
-     * @return the internal device tyoe
+     * @return the internal device type
      */
     public int getInternalType() {
         return mPort.type();
diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java
index 3cd615b..9774e80 100644
--- a/media/java/android/media/AudioProfile.java
+++ b/media/java/android/media/AudioProfile.java
@@ -18,6 +18,9 @@
 
 import android.annotation.NonNull;
 
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
 /**
  * An AudioProfile is specific to an audio format and lists supported sampling rates and
  * channel masks for that format.  An {@link AudioDeviceInfo} has a list of supported AudioProfiles.
@@ -63,4 +66,29 @@
     public @NonNull int[] getSampleRates() {
         return mSamplingRates;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("{");
+        sb.append(AudioFormat.toLogFriendlyEncoding(mFormat));
+        if (mSamplingRates != null && mSamplingRates.length > 0) {
+            sb.append(", sampling rates=").append(Arrays.toString(mSamplingRates));
+        }
+        if (mChannelMasks != null && mChannelMasks.length > 0) {
+            sb.append(", channel masks=").append(toHexString(mChannelMasks));
+        }
+        if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) {
+            sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks));
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    private static String toHexString(int[] ints) {
+        if (ints == null || ints.length == 0) {
+            return "";
+        }
+        return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X, ", anInt))
+                .collect(Collectors.joining());
+    }
 }
diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java
index 96bff4f..b899559 100644
--- a/media/java/android/media/MediaServiceManager.java
+++ b/media/java/android/media/MediaServiceManager.java
@@ -33,6 +33,7 @@
 public class MediaServiceManager {
     private static final String MEDIA_SESSION_SERVICE = "media_session";
     private static final String MEDIA_TRANSCODING_SERVICE = "media.transcoding";
+    private static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
 
     /**
      * @hide
@@ -79,4 +80,12 @@
     public ServiceRegisterer getMediaTranscodingServiceRegisterer() {
         return new ServiceRegisterer(MEDIA_TRANSCODING_SERVICE);
     }
+
+    /**
+     * Returns {@link ServiceRegisterer} for MEDIA_COMMUNICATION_SERVICE.
+     */
+    @NonNull
+    public ServiceRegisterer getMediaCommunicationServiceRegisterer() {
+        return new ServiceRegisterer(MEDIA_COMMUNICATION_SERVICE);
+    }
 }
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6b329f8..b1baf94 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -23,6 +23,8 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
@@ -39,6 +41,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Xml;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -99,6 +102,7 @@
     private int mSurfaceWidth;
     private int mSurfaceHeight;
     private final AttributeSet mAttrs;
+    private final XmlResourceParser mParser;
     private final int mDefStyleAttr;
     private int mWindowZOrder;
     private boolean mUseRequestedSurfaceLayout;
@@ -168,7 +172,16 @@
 
     public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mAttrs = attrs;
+        int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+        if (sourceResId != Resources.ID_NULL) {
+            Log.d(TAG, "Build local AttributeSet");
+            mParser  = context.getResources().getXml(sourceResId);
+            mAttrs = Xml.asAttributeSet(mParser);
+        } else {
+            Log.d(TAG, "Use passed in AttributeSet");
+            mParser = null;
+            mAttrs = attrs;
+        }
         mDefStyleAttr = defStyleAttr;
         resetSurfaceView();
         mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 96371e5..9f327c9 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -209,7 +209,7 @@
             prefix = "MONITOR_EVENT_",
             value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface MonitorEventTypeMask {}
+    public @interface MonitorEventMask {}
 
     /**
      * Monitor scrambling status change.
@@ -239,7 +239,7 @@
             int type, int subType, FilterConfiguration settings);
     private native int nativeGetId();
     private native long nativeGetId64Bit();
-    private native int nativeConfigureMonitorEvent(int monitorEventTypesMask);
+    private native int nativeConfigureMonitorEvent(int monitorEventMask);
     private native int nativeSetDataSource(Filter source);
     private native int nativeStartFilter();
     private native int nativeStopFilter();
@@ -344,19 +344,19 @@
      * will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version
      * information.
      *
-     * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to
-     *                              monitor it. Reset to stop monitoring.
+     * @param monitorEventMask Types of event to be monitored. Set corresponding bit to
+     *                         monitor it. Reset to stop monitoring.
      * @return result status of the operation.
      */
     @Result
-    public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) {
+    public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) {
         synchronized (mLock) {
             TunerUtils.checkResourceState(TAG, mIsClosed);
             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
-                    TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) {
+                    TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) {
                 return Tuner.RESULT_UNAVAILABLE;
             }
-            return nativeConfigureMonitorEvent(monitorEventTypesMask);
+            return nativeConfigureMonitorEvent(monitorEventMask);
         }
     }
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efd941e..757a90e 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -749,7 +749,8 @@
                  Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
                  Settings.Secure.SUPPRESS_DOZE,
                  Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
-                 Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT);
+                 Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+                 Settings.Secure.TRANSFORM_ENABLED);
 
     @Test
     public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 827cf4a..7bc7ba1 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,7 +16,7 @@
 -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#242424" /> <!-- 14% of white -->
+    <solid android:color="?android:attr/colorAccent" />
     <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
         android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
     <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml
new file mode 100644
index 0000000..1511659
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2021 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="@dimen/signal_icon_size"
+        android:height="@dimen/signal_icon_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.21,12.81c0.36,-0.16 0.69,-0.36 0.97,-0.61c0.41,-0.38 0.72,-0.83 0.94,-1.37c0.21,-0.54 0.32,-1.14 0.32,-1.79c0,-0.92 -0.16,-1.7 -0.49,-2.33c-0.32,-0.64 -0.79,-1.12 -1.43,-1.45c-0.62,-0.33 -1.4,-0.49 -2.32,-0.49H8.23V19h1.8v-5.76h2.5L15.06,19h1.92v-0.12L14.21,12.81zM10.03,11.71V6.32h2.18c0.59,0 1.06,0.11 1.42,0.34c0.36,0.22 0.62,0.54 0.78,0.95c0.16,0.41 0.24,0.89 0.24,1.44c0,0.49 -0.09,0.93 -0.27,1.34c-0.18,0.4 -0.46,0.73 -0.82,0.97c-0.36,0.23 -0.82,0.35 -1.37,0.35H10.03z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index bfd079b..5552020 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -77,4 +77,11 @@
             android:contentDescription="@string/data_connection_roaming"
             android:visibility="gone" />
     </FrameLayout>
+    <ImageView
+        android:id="@+id/mobile_roaming_large"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/stat_sys_roaming_large"
+        android:contentDescription="@string/data_connection_roaming"
+        android:visibility="gone" />
 </com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
index 42d541e3..10d49b3 100644
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -85,6 +85,13 @@
                 android:contentDescription="@string/data_connection_roaming"
                 android:visibility="gone" />
         </FrameLayout>
+        <ImageView
+            android:id="@+id/mobile_roaming_large"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/stat_sys_roaming_large"
+            android:contentDescription="@string/data_connection_roaming"
+            android:visibility="gone" />
     </com.android.keyguard.AlphaOptimizedLinearLayout>
 </com.android.systemui.statusbar.StatusBarMobileView>
 
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 49f9109..01e54ff 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -31,4 +31,9 @@
 
     <!-- AOD/Lockscreen alternate layout -->
     <bool name="flag_keyguard_layout">false</bool>
+
+    <bool name="flag_brightness_slider">false</bool>
+
+    <!-- People Tile flag -->
+    <bool name="flag_conversations">false</bool>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 036fcf3..78f7966 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -127,7 +127,7 @@
                     }
 
                     // If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
-                    // TODO(b/170396074): Remove this when we don't need a widget anymore.
+                    // TODO(b/170396074): Migrate to new feature flag (go/silk-flags-howto)
                     try {
                         int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
                                 Settings.Global.SHOW_PEOPLE_SPACE, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 055270d..7d06dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -137,7 +136,8 @@
                         mActivityTaskManager.getTasks(1);
                 if (!runningTasks.isEmpty()) {
                     final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                    if (!topPackage.contentEquals(clientPackage)) {
+                    if (!topPackage.contentEquals(clientPackage)
+                            && !Utils.isSystem(mContext, clientPackage)) {
                         Log.w(TAG, "Evicting client due to: " + topPackage);
                         mCurrentDialog.dismissWithoutCallback(true /* animate */);
                         mCurrentDialog = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index fd5e85a..076c7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -16,13 +16,16 @@
 
 package com.android.systemui.biometrics;
 
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.os.UserManager;
@@ -116,4 +119,10 @@
 
         return false;
     }
+
+    static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+        final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+                == PackageManager.PERMISSION_GRANTED;
+        return hasPermission && "android".equals(clientPackage);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 870e714..ec9291e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -21,6 +21,7 @@
 import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.LinearLayout
+import com.android.settingslib.Utils
 import com.android.systemui.R
 
 class OngoingPrivacyChip @JvmOverloads constructor(
@@ -36,8 +37,9 @@
                     R.dimen.ongoing_appops_chip_icon_margin_collapsed)
     private val iconSize =
             context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
-    private val iconColor = context.resources.getColor(
-            R.color.status_bar_clock_color, context.theme)
+    private val iconColor = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+    private val expandedIconColor = Utils
+            .getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
     private val sidePadding =
             context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
     private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
@@ -73,7 +75,7 @@
             iconsContainer.removeAllViews()
             chipBuilder.generateIcons().forEachIndexed { i, it ->
                 it.mutate()
-                it.setTint(iconColor)
+                it.setTint(if (expanded) expandedIconColor else iconColor)
                 val image = ImageView(context).apply {
                     setImageDrawable(it)
                     scaleType = ImageView.ScaleType.CENTER_INSIDE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 39b92dc..7d8d86f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -20,6 +20,7 @@
 import android.content.res.ColorStateList;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -65,10 +66,13 @@
         super.onFinishInflate();
         mDualToneHandler = new DualToneHandler(getContext());
         mMobileGroup = findViewById(R.id.mobile_combo);
+        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+        } else {
+            mMobileRoaming = findViewById(R.id.mobile_roaming);
+        }
         mMobileSignal = findViewById(R.id.mobile_signal);
-        mMobileRoaming = findViewById(R.id.mobile_roaming);
         mCarrierText = findViewById(R.id.qs_carrier_text);
-
         mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
 
         int colorForeground = Utils.getColorAttrDefaultColor(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index ebdcc00..77200cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -71,7 +71,7 @@
                         boolean activityIn, boolean activityOut,
                         CharSequence typeContentDescription,
                         CharSequence typeContentDescriptionHtml, CharSequence description,
-                        boolean isWide, int subId, boolean roaming) {
+                        boolean isWide, int subId, boolean roaming, boolean showTriangle) {
                     int slotIndex = getSlotIndex(subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index f742752..720c5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -268,7 +268,7 @@
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription,
                 CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming) {
+                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
             if (qsIcon == null) {
                 // Not data sim, don't display.
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1523278..5e1ca29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -249,7 +249,7 @@
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription,
                 CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming) {
+                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
             if (DEBUG) {
                 Log.d(TAG, "setMobileDataIndicators: "
                         + "statusIcon = " + (statusIcon == null ? "" :  statusIcon.toString()) + ","
@@ -263,7 +263,8 @@
                         + "description = " + description + ","
                         + "isWide = " + isWide + ","
                         + "subId = " + subId + ","
-                        + "roaming = " + roaming);
+                        + "roaming = " + roaming + ","
+                        + "showTriangle = " + showTriangle);
             }
             if (qsIcon == null) {
                 // Not data sim, don't display.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index fb281169..63e27796 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -47,6 +47,7 @@
 /** Quick settings tile: Enable/Disable NFC **/
 public class NfcTile extends QSTileImpl<BooleanState> {
 
+    private static final String NFC = "nfc";
     private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
 
     private NfcAdapter mAdapter;
@@ -89,7 +90,13 @@
 
     @Override
     public boolean isAvailable() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+        String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);
+        // For the restore from backup case
+        // Return false when "nfc" is not listed in quick_settings_tiles_stock.
+        if (stockTiles.contains(NFC)) {
+            return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+        }
+        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
index 11ee94c..8dcc8b4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
@@ -17,6 +17,7 @@
 package com.android.systemui.settings.brightness;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.settings.SecureSettings;
 
 import javax.inject.Inject;
@@ -28,11 +29,13 @@
 public class BrightnessControllerSettings {
 
     private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness";
+    private final FeatureFlags mFeatureFlags;
     private final boolean mUseThickSlider;
     private final boolean mUseMirrorOnThickSlider;
 
     @Inject
-    public BrightnessControllerSettings(SecureSettings settings) {
+    public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) {
+        mFeatureFlags = featureFlags;
         mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0;
         mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2;
     }
@@ -41,11 +44,11 @@
     // restart systemui after changing it.
     /** */
     boolean useThickSlider() {
-        return mUseThickSlider;
+        return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider();
     }
 
     /** */
     boolean useMirrorOnThickSlider() {
-        return mUseMirrorOnThickSlider;
+        return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 30b3158..e7b60c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -61,4 +61,13 @@
     public boolean isKeyguardLayoutEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
     }
+
+    /** b/178485354 */
+    public boolean useNewBrightnessSlider() {
+        return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
+    }
+
+    public boolean isPeopleTileEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_conversations);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index fdb793e..924eb26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -131,6 +131,7 @@
      */
     public void removeRemoteInput(NotificationEntry entry, Object token) {
         Objects.requireNonNull(entry);
+        if (entry.mRemoteEditImeVisible) return;
 
         pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 239addd..d562726 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -27,6 +27,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -64,7 +65,6 @@
         LayoutInflater inflater = LayoutInflater.from(context);
         StatusBarMobileView v = (StatusBarMobileView)
                 inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
-
         v.setSlot(slot);
         v.init();
         v.setVisibleState(STATE_ICON);
@@ -104,7 +104,11 @@
         mMobileGroup = findViewById(R.id.mobile_group);
         mMobile = findViewById(R.id.mobile_signal);
         mMobileType = findViewById(R.id.mobile_type);
-        mMobileRoaming = findViewById(R.id.mobile_roaming);
+        if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+        } else {
+            mMobileRoaming = findViewById(R.id.mobile_roaming);
+        }
         mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
         mIn = findViewById(R.id.mobile_in);
         mOut = findViewById(R.id.mobile_out);
@@ -160,7 +164,7 @@
         } else {
             mMobileType.setVisibility(View.GONE);
         }
-
+        mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE);
         mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
@@ -191,6 +195,7 @@
             }
         }
 
+        mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE);
         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
@@ -200,7 +205,8 @@
 
         needsLayout |= state.roaming != mState.roaming
                 || state.activityIn != mState.activityIn
-                || state.activityOut != mState.activityOut;
+                || state.activityOut != mState.activityOut
+                || state.showTriangle != mState.showTriangle;
 
         mState = state;
         return needsLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index bafa4a25..8a22b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -179,6 +179,8 @@
     private boolean mShelfIconVisible;
     private boolean mIsAlerting;
 
+    public boolean mRemoteEditImeVisible;
+
     /**
      * @param sbn the StatusBarNotification from system server
      * @param ranking also from system server
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 4fde118..db49e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -21,7 +21,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -50,7 +49,6 @@
     private final ShadeListBuilder mListBuilder;
     private final NotifCoordinators mNotifPluggableCoordinators;
     private final NotifInflaterImpl mNotifInflater;
-    private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
     private final DumpManager mDumpManager;
     private final ShadeViewManagerFactory mShadeViewManagerFactory;
     private final FeatureFlags mFeatureFlags;
@@ -64,7 +62,6 @@
             ShadeListBuilder listBuilder,
             NotifCoordinators notifCoordinators,
             NotifInflaterImpl notifInflater,
-            PeopleSpaceWidgetManager peopleSpaceWidgetManager,
             DumpManager dumpManager,
             ShadeViewManagerFactory shadeViewManagerFactory,
             FeatureFlags featureFlags) {
@@ -75,7 +72,6 @@
         mNotifPluggableCoordinators = notifCoordinators;
         mDumpManager = dumpManager;
         mNotifInflater = notifInflater;
-        mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
         mShadeViewManagerFactory = shadeViewManagerFactory;
         mFeatureFlags = featureFlags;
     }
@@ -103,7 +99,6 @@
         mListBuilder.attach(mNotifCollection);
         mNotifCollection.attach(mGroupCoalescer);
         mGroupCoalescer.attach(notificationService);
-        mPeopleSpaceWidgetManager.attach(notificationService);
 
         Log.d(TAG, "Notif pipeline initialized");
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 54ce4ed..0ad6507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -18,6 +18,7 @@
 
 import android.service.notification.StatusBarNotification
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.NotificationListener
@@ -73,7 +74,8 @@
     private val headsUpController: HeadsUpController,
     private val headsUpViewBinder: HeadsUpViewBinder,
     private val clickerBuilder: NotificationClicker.Builder,
-    private val animatedImageNotificationManager: AnimatedImageNotificationManager
+    private val animatedImageNotificationManager: AnimatedImageNotificationManager,
+    private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager
 ) : NotificationsController {
 
     override fun initialize(
@@ -126,6 +128,10 @@
 
             entryManager.attach(notificationListener)
         }
+
+        if (featureFlags.isPeopleTileEnabled) {
+            peopleSpaceWidgetManager.attach(notificationListener)
+        }
     }
 
     override fun dump(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index d11e864..5f90077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -221,7 +221,7 @@
             int qsType, boolean activityIn, boolean activityOut,
             CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml, CharSequence description,
-            boolean isWide, int subId, boolean roaming) {
+            boolean isWide, int subId, boolean roaming, boolean showTriangle) {
         if (DEBUG) {
             Log.d(TAG, "setMobileDataIndicators: "
                     + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
@@ -235,7 +235,8 @@
                     + "description = " + description + ","
                     + "isWide = " + isWide + ","
                     + "subId = " + subId + ","
-                    + "roaming = " + roaming);
+                    + "roaming = " + roaming + ","
+                    + "showTriangle = " + showTriangle);
         }
         MobileIconState state = getState(subId);
         if (state == null) {
@@ -250,6 +251,7 @@
         state.typeId = statusType;
         state.contentDescription = statusIcon.contentDescription;
         state.typeContentDescription = typeContentDescription;
+        state.showTriangle = showTriangle;
         state.roaming = roaming;
         state.activityIn = activityIn && mActivityEnabled;
         state.activityOut = activityOut && mActivityEnabled;
@@ -551,6 +553,7 @@
         public int subId;
         public int strengthId;
         public int typeId;
+        public boolean showTriangle;
         public boolean roaming;
         public boolean needsLeadingPadding;
         public CharSequence typeContentDescription;
@@ -569,20 +572,21 @@
                 return false;
             }
             MobileIconState that = (MobileIconState) o;
-            return subId == that.subId &&
-                    strengthId == that.strengthId &&
-                    typeId == that.typeId &&
-                    roaming == that.roaming &&
-                    needsLeadingPadding == that.needsLeadingPadding &&
-                    Objects.equals(typeContentDescription, that.typeContentDescription);
+            return subId == that.subId
+                    && strengthId == that.strengthId
+                    && typeId == that.typeId
+                    && showTriangle == that.showTriangle
+                    && roaming == that.roaming
+                    && needsLeadingPadding == that.needsLeadingPadding
+                    && Objects.equals(typeContentDescription, that.typeContentDescription);
         }
 
         @Override
         public int hashCode() {
 
             return Objects
-                    .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
-                            typeContentDescription);
+                    .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming,
+                            needsLeadingPadding, typeContentDescription);
         }
 
         public MobileIconState copy() {
@@ -596,6 +600,7 @@
             other.subId = subId;
             other.strengthId = strengthId;
             other.typeId = typeId;
+            other.showTriangle = showTriangle;
             other.roaming = roaming;
             other.needsLeadingPadding = needsLeadingPadding;
             other.typeContentDescription = typeContentDescription;
@@ -613,8 +618,9 @@
         }
 
         @Override public String toString() {
-            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
-                    + roaming + ", typeId=" + typeId + ", visible=" + visible + ")";
+            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId
+                    + ", showTriangle=" + showTriangle + ", roaming=" + roaming
+                    + ", typeId=" + typeId + ", visible=" + visible + ")";
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 5e88cd5..08a4492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -124,12 +124,13 @@
             final int statusType, final int qsType, final boolean activityIn,
             final boolean activityOut, final CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml, final CharSequence description,
-            final boolean isWide, final int subId, boolean roaming) {
+            final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
                         activityIn, activityOut, typeContentDescription,
-                        typeContentDescriptionHtml, description, isWide, subId, roaming);
+                        typeContentDescriptionHtml, description, isWide, subId, roaming,
+                        showTriangle);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 2f66508..39472de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -284,13 +284,15 @@
                     && !mCurrentState.carrierNetworkChangeMode
                     && mCurrentState.activityOut;
             showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
+            boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
+            int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+            showDataIcon |= mCurrentState.roaming;
             IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
                     getCurrentIconId(), contentDescription);
-            int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
             callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                     activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
                     description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
-                    mCurrentState.roaming);
+                    mCurrentState.roaming, showTriangle);
         } else {
             boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
             IconState statusIcon = new IconState(
@@ -316,10 +318,11 @@
                     && mCurrentState.activityOut;
             showDataIcon &= mCurrentState.isDefault || dataDisabled;
             int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+            boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
             callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                     activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
                     description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
-                    mCurrentState.roaming);
+                    mCurrentState.roaming, showTriangle);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index f2b0d76..e60d5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -66,12 +66,13 @@
          * @param isWide //TODO: unused?
          * @param subId subscription ID for which to update the UI
          * @param roaming indicates roaming
+         * @param showTriangle whether to show the mobile triangle the in status bar
          */
         default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription,
                 CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming) {
+                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
         }
 
         default void setSubs(List<SubscriptionInfo> subs) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 6c5251b..9380d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.Nullable;
@@ -50,6 +52,8 @@
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
@@ -61,6 +65,8 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.statusbar.IStatusBarService;
@@ -76,6 +82,7 @@
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -135,6 +142,27 @@
 
         mEditText = (RemoteEditText) getChildAt(0);
         mEditText.setInnerFocusable(false);
+        mEditText.setWindowInsetsAnimationCallback(
+                new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+            @NonNull
+            @Override
+            public WindowInsets onProgress(@NonNull WindowInsets insets,
+                    @NonNull List<WindowInsetsAnimation> runningAnimations) {
+                return insets;
+            }
+
+            @Override
+            public void onEnd(@NonNull WindowInsetsAnimation animation) {
+                super.onEnd(animation);
+                if (animation.getTypeMask() == WindowInsets.Type.ime()) {
+                    mEntry.mRemoteEditImeVisible =
+                            mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+                    if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
+                        mController.removeRemoteInput(mEntry, mToken);
+                    }
+                }
+            }
+        });
     }
 
     protected Intent prepareRemoteInputFromText() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 7042e2f..9669522 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -150,7 +150,7 @@
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
                 dataContentDescriptionHtml, description, icons.isWide,
-                mCurrentState.subId, /* roaming= */ false);
+                mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true);
     }
 
     private int getCurrentIconIdForCarrierWifi() {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 81ac21c..71835b6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -252,6 +252,7 @@
             public void onStop() {
                 mSysUiMainExecutor.execute(() -> {
                     if (oneHanded.isOneHandedEnabled()) {
+                        // Log metrics for 3-button navigation mode.
                         oneHanded.stopOneHanded(
                                 OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
                     } else if (oneHanded.isSwipeToNotificationEnabled()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 8a412bf..b452d3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -218,7 +218,7 @@
         mSignalCallback.setMobileDataIndicators(
                 mock(NetworkController.IconState.class),
                 mock(NetworkController.IconState.class),
-                0, 0, true, true, "", "", "", true, 0, true);
+                0, 0, true, true, "", "", "", true, 0, true, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
new file mode 100644
index 0000000..b37ac4a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 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.qs.tiles;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class NfcTileTest extends SysuiTestCase {
+
+    private static final String TILES_STOCK_WITHOUT_NFC = "wifi,cell,battery,dnd,flashlight,bt";
+    private static final String TILES_STOCK_WITH_NFC = "wifi,cell,battery,dnd,nfc,flashlight,bt";
+
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    @Mock
+    private QSTileHost mHost;
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private QSLogger mQSLogger;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+
+    private TestableLooper mTestableLooper;
+    private NfcTile mNfcTile;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mTestableLooper = TestableLooper.get(this);
+
+        when(mHost.getContext()).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+
+        mNfcTile = new NfcTile(
+                mHost,
+                mTestableLooper.getLooper(),
+                new Handler(mTestableLooper.getLooper()),
+                mMetricsLogger,
+                mStatusBarStateController,
+                mActivityStarter,
+                mQSLogger,
+                mBroadcastDispatcher
+        );
+    }
+
+    @Test
+    public void testIsAvailable_stockWithoutNfc_returnsFalse() {
+        when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn(
+                TILES_STOCK_WITHOUT_NFC);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true);
+        assertFalse(mNfcTile.isAvailable());
+    }
+
+    @Test
+    public void testIsAvailable_stockWithNfc_returnsTrue() {
+        when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn(
+                TILES_STOCK_WITH_NFC);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true);
+        assertTrue(mNfcTile.isAvailable());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index ebc45f4..c212cf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -125,7 +125,7 @@
         int subId = 5;
         boolean roaming = true;
         mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
-                typeDescriptionHtml, description, wide, subId, roaming);
+                typeDescriptionHtml, description, wide, subId, roaming, true);
         waitForCallbacks();
 
         ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
@@ -143,7 +143,7 @@
         Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
                 qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
                 outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(),
-                descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming));
+                descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true));
         assertEquals(status, statusArg.getValue());
         assertEquals(qs, qsArg.getValue());
         assertEquals(type, (int) typeIconArg.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index deabcbe..da1f5d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -489,7 +489,7 @@
                     anyInt(),
                     typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
                     any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
-                    anyBoolean(), anyInt(), anyBoolean());
+                    anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
                 false);
@@ -523,7 +523,7 @@
                 typeIconArg.capture(),
                 anyInt(), anyBoolean(), anyBoolean(),
                 any(CharSequence.class), any(CharSequence.class), any(),
-                anyBoolean(), anyInt(), eq(roaming));
+                anyBoolean(), anyInt(), eq(roaming), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = icon == -1 ? 0
                 : SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
@@ -544,7 +544,7 @@
                 typeIconArg.capture(),
                 anyInt(), anyBoolean(), anyBoolean(),
                 any(CharSequence.class), any(CharSequence.class), any(),
-                anyBoolean(), anyInt(), anyBoolean());
+                anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = SignalDrawable.getState(
                 level, CellSignalStrength.getNumSignalStrengthLevels(), !inet);
@@ -591,7 +591,7 @@
                 dataOutArg.capture(),
                 typeContentDescriptionArg.capture(),
                 typeContentDescriptionHtmlArg.capture(),
-                any(), anyBoolean(), anyInt(), anyBoolean());
+                any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
 
         IconState iconState = iconArg.getValue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 2b82f66..10166cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -134,7 +134,7 @@
         // Still be on wifi though.
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
-        verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0, true);
+        verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true);
     }
 
     @Test
diff --git a/services/Android.bp b/services/Android.bp
index b11a2e8..631146b 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
         ":services.profcollect-sources",
         ":services.restrictions-sources",
         ":services.searchui-sources",
+        ":services.speech-sources",
         ":services.startop.iorap-sources",
         ":services.systemcaptions-sources",
         ":services.translation-sources",
@@ -35,6 +36,7 @@
         ":services.usb-sources",
         ":services.voiceinteraction-sources",
         ":services.wifi-sources",
+        ":service-media-s-sources", // TODO (b/177640454)
         ":service-permission-sources",
         ":service-statsd-sources",
     ],
@@ -75,6 +77,7 @@
         "services.profcollect",
         "services.restrictions",
         "services.searchui",
+        "services.speech",
         "services.startop",
         "services.systemcaptions",
         "services.translation",
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 04e08ae..bf7aea6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,9 +17,14 @@
 
 package com.android.server.companion;
 
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.content.Context.BIND_IMPORTANT;
+
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
 import static com.android.internal.util.CollectionUtils.find;
 import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.internal.util.CollectionUtils.map;
 import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -40,12 +45,18 @@
 import android.app.role.RoleManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
 import android.companion.Association;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
+import android.companion.CompanionDeviceService;
 import android.companion.DeviceNotAssociatedException;
 import android.companion.ICompanionDeviceDiscoveryService;
 import android.companion.ICompanionDeviceManager;
+import android.companion.ICompanionDeviceService;
 import android.companion.IFindDeviceCallback;
 import android.content.ComponentName;
 import android.content.Context;
@@ -77,6 +88,7 @@
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.text.BidiFormatter;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
@@ -115,6 +127,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -135,6 +148,9 @@
             CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
             ".DeviceDiscoveryService");
 
+    private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
+    private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
+
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "CompanionDeviceManagerService";
 
@@ -146,18 +162,23 @@
     private static final String XML_ATTR_PACKAGE = "package";
     private static final String XML_ATTR_DEVICE = "device";
     private static final String XML_ATTR_PROFILE = "profile";
-    private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
+    private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
     private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
 
     private final CompanionDeviceManagerImpl mImpl;
     private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
     private PowerWhitelistManager mPowerWhitelistManager;
     private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
+    /** userId -> packageName -> serviceConnector */
+    private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
+            mDeviceListenerServiceConnectors;
     private IAppOpsService mAppOpsManager;
     private RoleManager mRoleManager;
     private BluetoothAdapter mBluetoothAdapter;
+    private UserManager mUserManager;
 
     private IFindDeviceCallback mFindDeviceCallback;
+    private ScanCallback mBleScanCallback = new BleScanCallback();
     private AssociationRequest mRequest;
     private String mCallingPackage;
     private AndroidFuture<Association> mOngoingDeviceDiscovery;
@@ -166,8 +187,14 @@
     private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
             new BluetoothDeviceConnectedListener();
     private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+    private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
+    private UnbindDeviceListenersRunnable
+            mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
+    private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
+            new ArrayMap<>();
 
     private final Object mLock = new Object();
+    private final Handler mMainHandler = Handler.getMain();
 
     /** userId -> [association] */
     @GuardedBy("mLock")
@@ -189,6 +216,7 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mPermissionControllerManager = requireNonNull(
                 context.getSystemService(PermissionControllerManager.class));
+        mUserManager = context.getSystemService(UserManager.class);
 
         Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
         mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -201,6 +229,16 @@
             }
         };
 
+        mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
+                ServiceConnector<ICompanionDeviceService>>>() {
+            @NonNull
+            @Override
+            protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
+                    int userId) {
+                return new ArrayMap<>();
+            }
+        };
+
         registerPackageMonitor();
     }
 
@@ -208,10 +246,13 @@
         new PackageMonitor() {
             @Override
             public void onPackageRemoved(String packageName, int uid) {
+                int userId = getChangingUserId();
                 updateAssociations(
                         as -> CollectionUtils.filter(as,
                                 a -> !Objects.equals(a.getPackageName(), packageName)),
-                        getChangingUserId());
+                        userId);
+
+                unbindDevicePresenceListener(packageName, userId);
             }
 
             @Override
@@ -225,6 +266,15 @@
         }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
     }
 
+    private void unbindDevicePresenceListener(String packageName, int userId) {
+        ServiceConnector<ICompanionDeviceService> deviceListener =
+                mDeviceListenerServiceConnectors.forUser(userId)
+                        .remove(packageName);
+        if (deviceListener != null) {
+            deviceListener.unbind();
+        }
+    }
+
     @Override
     public void onStart() {
         publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
@@ -238,6 +288,9 @@
                 mBluetoothAdapter.registerBluetoothConnectionCallback(
                         getContext().getMainExecutor(),
                         mBluetoothDeviceConnectedListener);
+                initBleScanning();
+            } else {
+                Log.w(LOG_TAG, "No BluetoothAdapter available");
             }
         }
     }
@@ -287,7 +340,7 @@
 
     @Override
     public void binderDied() {
-        Handler.getMain().post(this::cleanup);
+        mMainHandler.post(this::cleanup);
     }
 
     private void cleanup() {
@@ -399,7 +452,7 @@
                 checkCallerIsSystemOr(callingPackage, userId);
                 checkUsesFeature(callingPackage, getCallingUserId());
             }
-            return new ArrayList<>(CollectionUtils.map(
+            return new ArrayList<>(map(
                     getAllAssociations(userId, callingPackage),
                     a -> a.getDeviceMacAddress()));
         }
@@ -506,22 +559,18 @@
         public void registerDevicePresenceListenerService(
                 String packageName, String deviceAddress)
                 throws RemoteException {
-            checkCanRegisterObserverService(packageName, deviceAddress);
-
-            //TODO(eugenesusla) implement
+            registerDevicePresenceListenerActive(packageName, deviceAddress, true);
         }
 
         @Override
         public void unregisterDevicePresenceListenerService(
                 String packageName, String deviceAddress)
                 throws RemoteException {
-            checkCanRegisterObserverService(packageName, deviceAddress);
-
-            //TODO(eugenesusla) implement
+            registerDevicePresenceListenerActive(packageName, deviceAddress, false);
         }
 
-        private void checkCanRegisterObserverService(String packageName, String deviceAddress)
-                throws RemoteException {
+        private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
+                boolean active) throws RemoteException {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
                     "[un]registerDevicePresenceListenerService");
@@ -537,6 +586,20 @@
                         + " is not associated with device " + deviceAddress
                         + " for user " + userId));
             }
+
+            updateAssociations(associations -> map(associations, association -> {
+                if (Objects.equals(association.getPackageName(), packageName)
+                        && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+                    return new Association(
+                            association.getUserId(),
+                            association.getDeviceMacAddress(),
+                            association.getPackageName(),
+                            association.getDeviceProfile(),
+                            active /* notifyOnDeviceNearby */);
+                } else {
+                    return association;
+                }
+            }));
         }
 
         private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -693,6 +756,10 @@
         if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
             grantDeviceProfile(association);
         }
+
+        if (association.isNotifyOnDeviceNearby()) {
+            restartBleScan();
+        }
     }
 
     private void exemptFromAutoRevoke(String packageName, int uid) {
@@ -795,9 +862,9 @@
                                         association.getDeviceMacAddress());
                         if (association.getDeviceProfile() != null) {
                             tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
-                            tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
+                            tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
                                     Boolean.toString(
-                                            association.isKeepProfilePrivilegesWhenDeviceAway()));
+                                            association.isNotifyOnDeviceNearby()));
                         }
                         tag.endTag(null, XML_TAG_ASSOCIATION);
                     });
@@ -845,6 +912,14 @@
                 a -> Objects.equals(packageFilter, a.getPackageName()));
     }
 
+    private Set<Association> getAllAssociations() {
+        ArraySet<Association> result = new ArraySet<>();
+        for (UserInfo user : mUserManager.getAliveUsers()) {
+            result.addAll(getAllAssociations(user.id));
+        }
+        return result;
+    }
+
     private Set<Association> readAllAssociations(int userId) {
         final AtomicFile file = getStorageFileForUser(userId);
 
@@ -865,7 +940,7 @@
 
                     final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
                     final boolean persistentGrants = Boolean.valueOf(
-                            parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
+                            parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
 
                     if (appPackage == null || deviceAddress == null) continue;
 
@@ -896,6 +971,8 @@
                 }
             }
         }
+
+        onDeviceNearby(address);
     }
 
     private void grantDeviceProfile(Association association) {
@@ -919,6 +996,210 @@
 
     void onDeviceDisconnected(String address) {
         mCurrentlyConnectedDevices.remove(address);
+
+        onDeviceDisappeared(address);
+    }
+
+    private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
+            Association a) {
+        return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
+                a.getPackageName(),
+                pkg -> new ServiceConnector.Impl<>(getContext(),
+                        new Intent(CompanionDeviceService.SERVICE_INTERFACE),
+                        BIND_IMPORTANT,
+                        a.getUserId(),
+                        ICompanionDeviceService.Stub::asInterface));
+    }
+
+    private class BleScanCallback extends ScanCallback {
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "onScanResult(callbackType = "
+                        + callbackType + ", result = " + result + ")");
+            }
+
+            onDeviceNearby(result.getDevice().getAddress());
+        }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            for (int i = 0, size = results.size(); i < size; i++) {
+                onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i));
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
+                // ignore - this might happen if BT tries to auto-restore scans for us in the
+                // future
+            } else {
+                Log.wtf(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
+            }
+        }
+    }
+
+    private class UnbindDeviceListenersRunnable implements Runnable {
+
+        public String getJobId(String address) {
+            return "CDM_deviceGone_unbind_" + address;
+        }
+
+        @Override
+        public void run() {
+            int size = mDevicesLastNearby.size();
+            for (int i = 0; i < size; i++) {
+                String address = mDevicesLastNearby.keyAt(i);
+                Date lastNearby = mDevicesLastNearby.valueAt(i);
+
+                if (System.currentTimeMillis() - lastNearby.getTime()
+                        >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
+                    for (Association association : getAllAssociations(address)) {
+                        if (association.isNotifyOnDeviceNearby()) {
+                            getDeviceListenerServiceConnector(association).unbind();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private class TriggerDeviceDisappearedRunnable implements Runnable {
+
+        private final String mAddress;
+
+        TriggerDeviceDisappearedRunnable(String address) {
+            mAddress = address;
+        }
+
+        public void schedule() {
+            mMainHandler.removeCallbacks(this);
+            mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
+        }
+
+        @Override
+        public void run() {
+            onDeviceDisappeared(mAddress);
+        }
+    }
+
+    private Set<Association> getAllAssociations(String deviceAddress) {
+        List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
+        Set<Association> result = new ArraySet<>();
+        for (int i = 0, size = aliveUsers.size(); i < size; i++) {
+            UserInfo user = aliveUsers.get(i);
+            for (Association association : getAllAssociations(user.id)) {
+                if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+                    result.add(association);
+                }
+            }
+        }
+        return result;
+    }
+
+    private void onDeviceNearby(String address) {
+        Date timestamp = new Date();
+        mDevicesLastNearby.put(address, timestamp);
+
+        cancelUnbindDeviceListener(address);
+
+        mTriggerDeviceDisappearedRunnables
+                .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
+                .schedule();
+
+        for (Association association : getAllAssociations(address)) {
+            if (association.isNotifyOnDeviceNearby()) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Device " + address
+                            + " managed by " + association.getPackageName()
+                            + " is nearby on " + timestamp);
+                }
+                getDeviceListenerServiceConnector(association).run(
+                        service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+            }
+        }
+    }
+
+    private void onDeviceDisappeared(String address) {
+        boolean hasDeviceListeners = false;
+        for (Association association : getAllAssociations(address)) {
+            if (association.isNotifyOnDeviceNearby()) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Device " + address
+                            + " managed by " + association.getPackageName()
+                            + " disappeared; last seen on " + mDevicesLastNearby.get(address));
+                }
+
+                getDeviceListenerServiceConnector(association).run(
+                        service -> service.onDeviceDisappeared(address));
+                hasDeviceListeners = true;
+            }
+        }
+
+        cancelUnbindDeviceListener(address);
+        if (hasDeviceListeners) {
+            mMainHandler.postDelayed(
+                    mUnbindDeviceListenersRunnable,
+                    mUnbindDeviceListenersRunnable.getJobId(address),
+                    DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS);
+        }
+    }
+
+    private void cancelUnbindDeviceListener(String address) {
+        mMainHandler.removeCallbacks(
+                mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address));
+    }
+
+    private void initBleScanning() {
+        boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
+                new BluetoothAdapter.ServiceLifecycleCallback() {
+                    @Override
+                    public void onBluetoothServiceUp() {
+                        Log.i(LOG_TAG, "Bluetooth stack is up");
+                        startBleScan();
+                    }
+
+                    @Override
+                    public void onBluetoothServiceDown() {
+                        Log.w(LOG_TAG, "Bluetooth stack is down");
+                    }
+                });
+        if (bluetoothReady) {
+            startBleScan();
+        }
+    }
+
+    void startBleScan() {
+        List<ScanFilter> filters = getBleScanFilters();
+        if (filters.isEmpty()) {
+            return;
+        }
+        mBluetoothAdapter.getBluetoothLeScanner().startScan(
+                filters,
+                new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+                mBleScanCallback);
+    }
+
+    void restartBleScan() {
+        mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
+        startBleScan();
+    }
+
+    private List<ScanFilter> getBleScanFilters() {
+        ArrayList<ScanFilter> result = new ArrayList<>();
+        ArraySet<String> addressesSeen = new ArraySet<>();
+        for (Association association : getAllAssociations()) {
+            String address = association.getDeviceMacAddress();
+            if (addressesSeen.contains(address)) {
+                continue;
+            }
+            if (association.isNotifyOnDeviceNearby()) {
+                result.add(new ScanFilter.Builder().setDeviceAddress(address).build());
+                addressesSeen.add(address);
+            }
+        }
+        return result;
     }
 
     private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 50e5557..c287240 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -254,6 +254,8 @@
         mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
         mStats.setPowerProfileLocked(new PowerProfile(context));
+        mStats.startTrackingSystemServerCpuTime();
+
         mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 6905b3d..6851d71 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -90,6 +90,7 @@
             int userId, PromptInfo promptInfo, String opPackageName,
             boolean checkDevicePolicyManager)
             throws RemoteException {
+
         final boolean confirmationRequested = promptInfo.isConfirmationRequested();
         final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
         final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
@@ -111,7 +112,7 @@
 
                 @AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
                         devicePolicyManager, settingObserver, sensor, userId, opPackageName,
-                        checkDevicePolicyManager, requestedStrength);
+                        checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId());
 
                 Slog.d(TAG, "Package: " + opPackageName
                         + " Sensor ID: " + sensor.id
@@ -141,7 +142,11 @@
             DevicePolicyManager devicePolicyManager,
             BiometricService.SettingObserver settingObserver,
             BiometricSensor sensor, int userId, String opPackageName,
-            boolean checkDevicePolicyManager, int requestedStrength) {
+            boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) {
+
+        if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) {
+            return BIOMETRIC_NO_HARDWARE;
+        }
 
         final boolean wasStrongEnough =
                 Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index d87af42..5cd0bbf 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -399,10 +399,15 @@
         }
     }
 
-    public static boolean isKeyguard(Context context, String clientPackage) {
-        final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
-                == PackageManager.PERMISSION_GRANTED;
-
+    /**
+     * Checks if a client package matches Keyguard and can perform internal biometric operations.
+     *
+     * @param context The system context.
+     * @param clientPackage The name of the package to be checked against Keyguard.
+     * @return Whether the given package matches Keyguard.
+     */
+    public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) {
+        final boolean hasPermission = hasInternalPermission(context);
         final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                 context.getResources().getString(R.string.config_keyguardComponent));
         final String keyguardPackage = keyguardComponent != null
@@ -410,6 +415,34 @@
         return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
     }
 
+    /**
+     * Checks if a client package matches the Android system and can perform internal biometric
+     * operations.
+     *
+     * @param context The system context.
+     * @param clientPackage The name of the package to be checked against the Android system.
+     * @return Whether the given package matches the Android system.
+     */
+    public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+        return hasInternalPermission(context) && "android".equals(clientPackage);
+    }
+
+    /**
+     * Checks if a client package matches Settings and can perform internal biometric operations.
+     *
+     * @param context The system context.
+     * @param clientPackage The name of the package to be checked against Settings.
+     * @return Whether the given package matches Settings.
+     */
+    public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) {
+        return hasInternalPermission(context) && "com.android.settings".equals(clientPackage);
+    }
+
+    private static boolean hasInternalPermission(@NonNull Context context) {
+        return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     public static String getClientName(@Nullable BaseClientMonitor client) {
         return client != null ? client.getClass().getSimpleName() : "null";
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 14433fb..0536e78 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -149,9 +149,10 @@
                 pm.incrementAuthForUser(getTargetUserId(), authenticated);
             }
 
-            // Ensure authentication only succeeds if the client activity is on top or is keyguard.
+            // Ensure authentication only succeeds if the client activity is on top.
             boolean isBackgroundAuth = false;
-            if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
+            if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())
+                    && !Utils.isSystem(getContext(), getOwnerString())) {
                 final List<ActivityManager.RunningTaskInfo> tasks =
                         mActivityTaskManager.getTasks(1);
                 if (tasks == null || tasks.isEmpty()) {
@@ -166,7 +167,7 @@
                         final String topPackage = topActivity.getPackageName();
                         if (!topPackage.contentEquals(getOwnerString())) {
                             Slog.e(TAG, "Background authentication detected, top: " + topPackage
-                                    + ", client: " + this);
+                                    + ", client: " + getOwnerString());
                             isBackgroundAuth = true;
                         }
                     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 0265cb9..686f9f5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -25,6 +25,10 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +36,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricService;
@@ -49,6 +54,7 @@
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Build;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
@@ -80,6 +86,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -219,8 +226,8 @@
         @SuppressWarnings("deprecation")
         @Override // Binder call
         public void authenticate(final IBinder token, final long operationId,
-                @FingerprintManager.SensorId final int sensorId, final int userId,
-                final IFingerprintServiceReceiver receiver, final String opPackageName) {
+                final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
+                final String opPackageName) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
@@ -236,7 +243,7 @@
             final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
 
             // Clear calling identity when checking LockPatternUtils for StrongAuth flags.
-            final long identity = Binder.clearCallingIdentity();
+            long identity = Binder.clearCallingIdentity();
             try {
                 if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
                     // If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -266,9 +273,101 @@
                 return;
             }
 
-            provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
-                    0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
-                    restricted, statsClient, isKeyguard);
+            final FingerprintSensorPropertiesInternal sensorProps =
+                    provider.second.getSensorProperties(sensorId);
+            if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
+                    && sensorProps != null && sensorProps.isAnyUdfpsType()) {
+                identity = Binder.clearCallingIdentity();
+                try {
+                    authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } else {
+                provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+                        0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+                        restricted, statsClient, isKeyguard);
+            }
+        }
+
+        private void authenticateWithPrompt(
+                final long operationId,
+                @NonNull final FingerprintSensorPropertiesInternal props,
+                final int userId,
+                final IFingerprintServiceReceiver receiver) {
+
+            final Context context = getUiContext();
+            final Executor executor = context.getMainExecutor();
+
+            final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context)
+                    .setTitle(context.getString(R.string.biometric_dialog_default_title))
+                    .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle))
+                    .setNegativeButton(
+                            context.getString(R.string.cancel),
+                            executor,
+                            (dialog, which) -> {
+                                try {
+                                    receiver.onError(
+                                            FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */);
+                                } catch (RemoteException e) {
+                                    Slog.e(TAG, "Remote exception in negative button onClick()", e);
+                                }
+                            })
+                    .setSensorId(props.sensorId)
+                    .build();
+
+            final BiometricPrompt.AuthenticationCallback promptCallback =
+                    new BiometricPrompt.AuthenticationCallback() {
+                        @Override
+                        public void onAuthenticationError(int errorCode, CharSequence errString) {
+                            try {
+                                if (FingerprintUtils.isKnownErrorCode(errorCode)) {
+                                    receiver.onError(errorCode, 0 /* vendorCode */);
+                                } else {
+                                    receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode);
+                                }
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationError()", e);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationSucceeded(
+                                BiometricPrompt.AuthenticationResult result) {
+                            final Fingerprint fingerprint = new Fingerprint("", 0, 0L);
+                            final boolean isStrong = props.sensorStrength == STRENGTH_STRONG;
+                            try {
+                                receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationFailed() {
+                            try {
+                                receiver.onAuthenticationFailed();
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationAcquired(int acquireInfo) {
+                            try {
+                                if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) {
+                                    receiver.onAcquired(acquireInfo, 0 /* vendorCode */);
+                                } else {
+                                    receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo);
+                                }
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e);
+                            }
+                        }
+                    };
+
+            biometricPrompt.authenticateUserForOperation(
+                    new CancellationSignal(), executor, promptCallback, userId, operationId);
         }
 
         @Override
@@ -374,6 +473,7 @@
         @Override // Binder call
         public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
                 final String opPackageName) {
+
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index dc6fd3a..d69151d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -16,8 +16,18 @@
 
 package com.android.server.biometrics.sensors.fingerprint;
 
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
 import android.hardware.fingerprint.Fingerprint;
 import android.text.TextUtils;
 import android.util.SparseArray;
@@ -138,5 +148,51 @@
             return state;
         }
     }
+
+    /**
+     * Checks if the given error code corresponds to a known fingerprint error.
+     *
+     * @param errorCode The error code to be checked.
+     * @return Whether the error code corresponds to a known error.
+     */
+    public static boolean isKnownErrorCode(int errorCode) {
+        switch (errorCode) {
+            case FingerprintError.ERROR_HW_UNAVAILABLE:
+            case FingerprintError.ERROR_UNABLE_TO_PROCESS:
+            case FingerprintError.ERROR_TIMEOUT:
+            case FingerprintError.ERROR_NO_SPACE:
+            case FingerprintError.ERROR_CANCELED:
+            case FingerprintError.ERROR_UNABLE_TO_REMOVE:
+            case FingerprintError.ERROR_LOCKOUT:
+            case FingerprintError.ERROR_VENDOR:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks if the given acquired code corresponds to a known fingerprint error.
+     *
+     * @param acquiredCode The acquired code to be checked.
+     * @return Whether the acquired code corresponds to a known error.
+     */
+    public static boolean isKnownAcquiredCode(int acquiredCode) {
+        switch (acquiredCode) {
+            case FINGERPRINT_ACQUIRED_GOOD:
+            case FINGERPRINT_ACQUIRED_PARTIAL:
+            case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+            case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+            case FINGERPRINT_ACQUIRED_TOO_SLOW:
+            case FINGERPRINT_ACQUIRED_TOO_FAST:
+            case FINGERPRINT_ACQUIRED_VENDOR:
+            case FINGERPRINT_ACQUIRED_START:
+                return true;
+
+            default:
+                return false;
+        }
+    }
 }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f845024..fec2c46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -96,7 +96,8 @@
                         Slog.e(getTag(), "Task stack changed for client: " + client);
                         continue;
                     }
-                    if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+                    if (Utils.isKeyguard(mContext, client.getOwnerString())
+                            || Utils.isSystem(mContext, client.getOwnerString())) {
                         continue; // Keyguard is always allowed
                     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index a4a8401..6eb68ca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -123,7 +123,8 @@
                     Slog.e(TAG, "Task stack changed for client: " + client);
                     return;
                 }
-                if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+                if (Utils.isKeyguard(mContext, client.getOwnerString())
+                        || Utils.isSystem(mContext, client.getOwnerString())) {
                     return; // Keyguard is always allowed
                 }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 23c70ee..2e4200c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -289,6 +289,8 @@
     private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
     private static native boolean nativeIsVibrating(long ptr, int deviceId);
     private static native int[] nativeGetVibratorIds(long ptr, int deviceId);
+    private static native int nativeGetBatteryCapacity(long ptr, int deviceId);
+    private static native int nativeGetBatteryStatus(long ptr, int deviceId);
     private static native void nativeReloadKeyboardLayouts(long ptr);
     private static native void nativeReloadDeviceAliases(long ptr);
     private static native String nativeDump(long ptr);
@@ -1818,8 +1820,7 @@
     }
 
     private void updateMaximumObscuringOpacityForTouchFromSettings() {
-        final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch(
-                mContext);
+        final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch();
         if (opacity < 0 || opacity > 1) {
             Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
                     + ", it should be >= 0 and <= 1, rejecting update.");
@@ -2009,6 +2010,18 @@
 
     // Binder call
     @Override
+    public int getBatteryStatus(int deviceId) {
+        return nativeGetBatteryStatus(mPtr, deviceId);
+    }
+
+    // Binder call
+    @Override
+    public int getBatteryCapacity(int deviceId) {
+        return nativeGetBatteryCapacity(mPtr, deviceId);
+    }
+
+    // Binder call
+    @Override
     public void setPointerIconType(int iconId) {
         nativeSetPointerIconType(mPtr, iconId);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e32c00f..8fa3a00 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -9302,21 +9302,30 @@
                 Slog.v(TAG, "onNotificationEnqueuedLocked() called with: r = [" + r + "]");
             }
             final StatusBarNotification sbn = r.getSbn();
-            notifyAssistantLocked(
-                    sbn,
-                    r.getNotificationType(),
-                    true /* sameUserOnly */,
-                    (assistant, sbnHolder) -> {
-                        try {
-                            if (debug) {
-                                Slog.v(TAG,
-                                        "calling onNotificationEnqueuedWithChannel " + sbnHolder);
-                            }
-                            assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
-                        } catch (RemoteException ex) {
-                            Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+
+            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+                boolean sbnVisible = isVisibleToListener(
+                        sbn, r.getNotificationType(), info)
+                        && info.isSameUser(r.getUserId());
+                if (sbnVisible) {
+                    TrimCache trimCache = new TrimCache(sbn);
+                    final INotificationListener assistant = (INotificationListener) info.service;
+                    final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+                    final StatusBarNotificationHolder sbnHolder =
+                            new StatusBarNotificationHolder(sbnToPost);
+                    try {
+                        if (debug) {
+                            Slog.v(TAG,
+                                    "calling onNotificationEnqueuedWithChannel " + sbnHolder);
                         }
-                    });
+                        final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+                        assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel(),
+                                update);
+                    } catch (RemoteException ex) {
+                        Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+                    }
+                }
+            }
         }
 
         @GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c27e670..b8d3e54 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -28304,6 +28304,13 @@
                     }
                     continue;
                 }
+                if (ps.appId < Process.FIRST_APPLICATION_UID) {
+                    if (DEBUG_PER_UID_READ_TIMEOUTS) {
+                        Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId);
+                    }
+                    continue;
+                }
+
                 final AndroidPackage pkg = ps.getPkg();
                 if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
                         || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2929568..04f399c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3165,6 +3165,17 @@
             mReadMessages.append("Error reading: " + e.toString());
             PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
             Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+        } finally {
+            if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) {
+                Slog.wtf(PackageManagerService.TAG,
+                        "No internal VersionInfo found in settings, using current.");
+                findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+            }
+            if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) {
+                Slog.wtf(PackageManagerService.TAG,
+                        "No external VersionInfo found in settings, using current.");
+                findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
+            }
         }
 
         // If the build is setup to drop runtime permissions
diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp
new file mode 100644
index 0000000..379b075
--- /dev/null
+++ b/services/core/java/com/android/server/speech/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "services.speech-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.speech",
+    defaults: ["platform_service_defaults"],
+    srcs: [":services.speech-sources"],
+    libs: ["services.core"],
+}
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
new file mode 100644
index 0000000..96248c3
--- /dev/null
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.speech.IRecognitionListener;
+import android.speech.IRecognitionService;
+import android.speech.RecognitionService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> {
+    private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
+    private static final boolean DEBUG = true;
+
+    RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) {
+        super(context,
+                new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
+                Context.BIND_AUTO_CREATE
+                        | Context.BIND_FOREGROUND_SERVICE
+                        | Context.BIND_INCLUDE_CAPABILITIES
+                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+                userId,
+                IRecognitionService.Stub::asInterface);
+
+        if (DEBUG) {
+            Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString());
+        }
+    }
+
+    void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
+            String featureId) throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId);
+        }
+        run(service -> service.startListening(recognizerIntent, listener, packageName, featureId));
+    }
+
+    void stopListening(IRecognitionListener listener, String packageName, String featureId)
+            throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
+        }
+        run(service -> service.stopListening(listener, packageName, featureId));
+    }
+
+    void cancel(IRecognitionListener listener, String packageName, String featureId)
+            throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
+        }
+        run(service -> service.cancel(listener, packageName, featureId));
+    }
+
+    @Override // from ServiceConnector.Impl
+    protected void onServiceConnectionStatusChanged(
+            IRecognitionService service, boolean connected) {
+        if (!DEBUG) {
+            return;
+        }
+
+        if (connected) {
+            Slog.i(TAG, "Connected to ASR service");
+        } else {
+            Slog.w(TAG, "Disconnected from ASR service");
+        }
+    }
+
+    @Override // from AbstractRemoteService
+    protected long getAutoDisconnectTimeoutMs() {
+        return PERMANENT_BOUND_TIMEOUT_MS;
+    }
+}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
new file mode 100644
index 0000000..592ba9e
--- /dev/null
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.UserHandle;
+import android.speech.IRecognitionServiceManager;
+import android.speech.IRecognitionServiceManagerCallback;
+
+import com.android.internal.R;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+/**
+ * System service implementation for Speech Recognizer.
+ *
+ * <p>This service uses RemoteSpeechRecognitionService to bind to a default implementation of
+ * ISpeechRecognition. It relays all the requests from the client to the default system impl of
+ * ISpeechRecognition service (denoted by {@code
+ * R.string.config_defaultOnDeviceSpeechRecognitionService}).
+ */
+public final class SpeechRecognitionManagerService extends
+        AbstractMasterSystemService<SpeechRecognitionManagerService,
+                SpeechRecognitionManagerServiceImpl> {
+    private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName();
+
+    public SpeechRecognitionManagerService(@NonNull Context context) {
+        super(context,
+                new FrameworkResourcesServiceNameResolver(
+                        context,
+                        R.string.config_defaultOnDeviceSpeechRecognitionService),
+                /*disallowProperty=*/ null);
+    }
+
+    @Override // from SystemService
+    public void onStart() {
+        SpeechRecognitionManagerServiceStub serviceStub = new SpeechRecognitionManagerServiceStub();
+        publishBinderService(Context.SPEECH_RECOGNITION_SERVICE, serviceStub);
+    }
+
+    @Override
+    protected SpeechRecognitionManagerServiceImpl newServiceLocked(
+            @UserIdInt int resolvedUserId, boolean disabled) {
+        return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
+    }
+
+    final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
+
+        @Override
+        public void createSession(IRecognitionServiceManagerCallback callback) {
+            int userId = UserHandle.getCallingUserId();
+            synchronized (mLock) {
+                SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId);
+                service.createSessionLocked(callback);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
new file mode 100644
index 0000000..bcaf174
--- /dev/null
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.speech.IRecognitionListener;
+import android.speech.IRecognitionService;
+import android.speech.IRecognitionServiceManagerCallback;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+final class SpeechRecognitionManagerServiceImpl extends
+        AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl,
+            SpeechRecognitionManagerService> {
+
+    private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName();
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteSpeechRecognitionService mRemoteService;
+
+    SpeechRecognitionManagerServiceImpl(
+            @NonNull SpeechRecognitionManagerService master,
+            @NonNull Object lock, @UserIdInt int userId, boolean disabled) {
+        super(master, lock, userId);
+        updateRemoteServiceLocked();
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        try {
+            return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        final boolean enabledChanged = super.updateLocked(disabled);
+        updateRemoteServiceLocked();
+        return enabledChanged;
+    }
+
+    /**
+     * Updates the reference to the remote service.
+     */
+    @GuardedBy("mLock")
+    private void updateRemoteServiceLocked() {
+        if (mRemoteService != null) {
+            if (mMaster.debug) {
+                Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+            }
+            mRemoteService.unbind();
+            mRemoteService = null;
+        }
+    }
+
+    void createSessionLocked(IRecognitionServiceManagerCallback callback) {
+        // TODO(b/176578753): check clients have record audio permission.
+        // TODO(b/176578753): verify caller package is the one supplied
+
+        RemoteSpeechRecognitionService service = ensureRemoteServiceLocked();
+
+        if (service == null) {
+            tryRespondWithError(callback);
+            return;
+        }
+
+        service.connect().thenAccept(binderService -> {
+            if (binderService != null) {
+                try {
+                    callback.onSuccess(new IRecognitionService.Stub() {
+                        @Override
+                        public void startListening(Intent recognizerIntent,
+                                IRecognitionListener listener,
+                                String packageName, String featureId) throws RemoteException {
+                            service.startListening(
+                                    recognizerIntent, listener, packageName, featureId);
+                        }
+
+                        @Override
+                        public void stopListening(IRecognitionListener listener,
+                                String packageName,
+                                String featureId) throws RemoteException {
+                            service.stopListening(listener, packageName, featureId);
+                        }
+
+                        @Override
+                        public void cancel(IRecognitionListener listener,
+                                String packageName,
+                                String featureId) throws RemoteException {
+                            service.cancel(listener, packageName, featureId);
+                        }
+                    });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error creating a speech recognition session", e);
+                    tryRespondWithError(callback);
+                }
+            } else {
+                tryRespondWithError(callback);
+            }
+        });
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteSpeechRecognitionService ensureRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
+                }
+                return null;
+            }
+            final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+            mRemoteService =
+                    new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId);
+        }
+        return mRemoteService;
+    }
+
+    private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) {
+        try {
+            callback.onError();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to respond with error");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 5723e1d..ee30fa2 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -50,8 +50,6 @@
      */
     private final int mProcessId;
 
-    private boolean mIsForeground;
-
     /**
      * All the clients that share the same resource would be under the same group id.
      *
@@ -90,6 +88,12 @@
     private int mUsingCiCamId = INVALID_RESOURCE_ID;
 
     /**
+     * If the priority is overwritten through
+     * {@link TunerResourceManagerService#setPriority(int, int)}.
+     */
+    private boolean mIsPriorityOverwritten = false;
+
+    /**
      * Optional arbitrary priority value given by the client.
      *
      * <p>This value can override the default priorotiy calculated from
@@ -121,17 +125,10 @@
     }
 
     /**
-     * Set the current isForeground status.
+     * If the client priority is overwrttien.
      */
-    public void setForeground(boolean isForeground) {
-        mIsForeground = isForeground;
-    }
-
-    /**
-     * Get the previous recorded isForeground status.
-     */
-    public boolean isForeground() {
-        return mIsForeground;
+    public boolean isPriorityOverwritten() {
+        return mIsPriorityOverwritten;
     }
 
     public int getGroupId() {
@@ -153,6 +150,17 @@
         mPriority = priority;
     }
 
+    /**
+     * Overwrite the client priority.
+     */
+    public void overwritePriority(int priority) {
+        if (priority < 0) {
+            return;
+        }
+        mIsPriorityOverwritten = true;
+        mPriority = priority;
+    }
+
     public void setNiceValue(int niceValue) {
         mNiceValue = niceValue;
     }
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 988582d..0c04b07 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -507,9 +507,8 @@
                                               .useCase(profile.useCase)
                                               .processId(pid)
                                               .build();
-        clientProfile.setForeground(checkIsForeground(pid));
         clientProfile.setPriority(
-                getClientPriority(profile.useCase, clientProfile.isForeground()));
+                getClientPriority(profile.useCase, checkIsForeground(pid)));
 
         addClientProfile(clientId[0], clientProfile, listener);
     }
@@ -547,8 +546,7 @@
             return false;
         }
 
-        profile.setForeground(checkIsForeground(profile.getProcessId()));
-        profile.setPriority(priority);
+        profile.overwritePriority(priority);
         profile.setNiceValue(niceValue);
 
         return true;
@@ -694,7 +692,7 @@
                 } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
                     // Record the frontend id with the lowest client priority among all the
                     // in use frontends when no available frontend has been found.
-                    int priority = getOwnerClientPriority(fr.getOwnerClientId());
+                    int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId());
                     if (currentLowestPriority > priority) {
                         inUseLowestPriorityFrHandle = fr.getHandle();
                         currentLowestPriority = priority;
@@ -760,7 +758,7 @@
             } else {
                 // Record the lnb id with the lowest client priority among all the
                 // in use lnb when no available lnb has been found.
-                int priority = getOwnerClientPriority(lnb.getOwnerClientId());
+                int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
                 if (currentLowestPriority > priority) {
                     inUseLowestPriorityLnbHandle = lnb.getHandle();
                     currentLowestPriority = priority;
@@ -818,7 +816,7 @@
         }
         for (int ownerId : cas.getOwnerClientIds()) {
             // Record the client id with lowest priority that is using the current Cas system.
-            int priority = getOwnerClientPriority(ownerId);
+            int priority = updateAndGetOwnerClientPriority(ownerId);
             if (currentLowestPriority > priority) {
                 lowestPriorityOwnerId = ownerId;
                 currentLowestPriority = priority;
@@ -867,7 +865,7 @@
         }
         for (int ownerId : ciCam.getOwnerClientIds()) {
             // Record the client id with lowest priority that is using the current Cas system.
-            int priority = getOwnerClientPriority(ownerId);
+            int priority = updateAndGetOwnerClientPriority(ownerId);
             if (currentLowestPriority > priority) {
                 lowestPriorityOwnerId = ownerId;
                 currentLowestPriority = priority;
@@ -966,18 +964,17 @@
     }
 
     @VisibleForTesting
-    // This mothod is to sync up the request client's foreground/background status and update
-    // the client priority accordingly whenever new resource request comes in.
-    protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) {
-        int pid = requestProfile.getProcessId();
-        boolean currentIsForeground = checkIsForeground(pid);
-        if (requestProfile.isForeground() == currentIsForeground) {
+    // This mothod is to sync up the request/holder client's foreground/background status and update
+    // the client priority accordingly whenever a new resource request comes in.
+    protected void clientPriorityUpdateOnRequest(ClientProfile profile) {
+        if (profile.isPriorityOverwritten()) {
             // To avoid overriding the priority set through updateClientPriority API.
             return;
         }
-        requestProfile.setForeground(currentIsForeground);
-        requestProfile.setPriority(
-                getClientPriority(requestProfile.getUseCase(), currentIsForeground));
+        int pid = profile.getProcessId();
+        boolean currentIsForeground = checkIsForeground(pid);
+        profile.setPriority(
+                getClientPriority(profile.getUseCase(), currentIsForeground));
     }
 
     @VisibleForTesting
@@ -1154,13 +1151,15 @@
     }
 
     /**
-     * Get the owner client's priority.
+     * Update and get the owner client's priority.
      *
      * @param clientId the owner client id.
      * @return the priority of the owner client.
      */
-    private int getOwnerClientPriority(int clientId) {
-        return getClientProfile(clientId).getPriority();
+    private int updateAndGetOwnerClientPriority(int clientId) {
+        ClientProfile profile = getClientProfile(clientId);
+        clientPriorityUpdateOnRequest(profile);
+        return profile.getPriority();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 106db0b..058324c 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -180,6 +180,7 @@
         } else if (launchMode == WINDOWING_MODE_FULLSCREEN) {
             if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
         } else if (layout != null && canApplyFreeformPolicy) {
+            mTmpBounds.set(currentParams.mBounds);
             getLayoutBounds(display, root, layout, mTmpBounds);
             if (!mTmpBounds.isEmpty()) {
                 launchMode = WINDOWING_MODE_FREEFORM;
@@ -492,11 +493,11 @@
     }
 
     private void getLayoutBounds(@NonNull DisplayContent display, @NonNull ActivityRecord root,
-            @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect outBounds) {
+            @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds) {
         final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
         final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
         if (!windowLayout.hasSpecifiedSize() && verticalGravity == 0 && horizontalGravity == 0) {
-            outBounds.setEmpty();
+            inOutBounds.setEmpty();
             return;
         }
 
@@ -510,11 +511,17 @@
         int width;
         int height;
         if (!windowLayout.hasSpecifiedSize()) {
-            outBounds.setEmpty();
-            getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
-                    /* hasInitialBounds */ false, outBounds);
-            width = outBounds.width();
-            height = outBounds.height();
+            if (!inOutBounds.isEmpty()) {
+                // If the bounds is resolved already and WindowLayout doesn't have any opinion on
+                // its size, use the already resolved size and apply the gravity to it.
+                width = inOutBounds.width();
+                height = inOutBounds.height();
+            } else {
+                getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
+                        /* hasInitialBounds */ false, inOutBounds);
+                width = inOutBounds.width();
+                height = inOutBounds.height();
+            }
         } else {
             width = defaultWidth;
             if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
@@ -555,11 +562,11 @@
                 fractionOfVerticalOffset = 0.5f;
         }
 
-        outBounds.set(0, 0, width, height);
-        outBounds.offset(displayStableBounds.left, displayStableBounds.top);
+        inOutBounds.set(0, 0, width, height);
+        inOutBounds.offset(displayStableBounds.left, displayStableBounds.top);
         final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
         final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
-        outBounds.offset(xOffset, yOffset);
+        inOutBounds.offset(xOffset, yOffset);
     }
 
     /**
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index dc15b07..5b587e9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -26,14 +26,14 @@
 // Log debug messages about InputDispatcherPolicy
 #define DEBUG_INPUT_DISPATCHER_POLICY 0
 
-
-#include <atomic>
-#include <cinttypes>
-#include <limits.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android/os/IInputConstants.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include <limits.h>
+#include <atomic>
+#include <cinttypes>
 
 #include <utils/Log.h>
 #include <utils/Looper.h>
@@ -46,6 +46,7 @@
 #include <input/SpriteController.h>
 #include <ui/Region.h>
 
+#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
 #include <inputflinger/InputManager.h>
 
 #include <android_os_MessageQueue.h>
@@ -1908,6 +1909,20 @@
     return vibIdArray;
 }
 
+static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryCapacity(deviceId);
+    return static_cast<jint>(ret.value_or(android::os::IInputConstants::INVALID_BATTERY_CAPACITY));
+}
+
+static jint nativeGetBatteryStatus(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryStatus(deviceId);
+    return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN));
+}
+
 static void nativeReloadKeyboardLayouts(JNIEnv* /* env */,
         jclass /* clazz */, jlong ptr) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -2163,6 +2178,8 @@
         {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
         {"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating},
         {"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds},
+        {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity},
+        {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus},
         {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
         {"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},
         {"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c3bb757..14376d3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -35,10 +35,8 @@
 import static android.app.admin.DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED;
 import static android.app.admin.DevicePolicyManager.CODE_NONSYSTEM_USER_EXISTS;
 import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT;
 import static android.app.admin.DevicePolicyManager.CODE_OK;
 import static android.app.admin.DevicePolicyManager.CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
-import static android.app.admin.DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
 import static android.app.admin.DevicePolicyManager.CODE_SYSTEM_USER;
 import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
 import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
@@ -1380,11 +1378,6 @@
             SystemProperties.set(key, value);
         }
 
-        // TODO (b/137101239): clean up split system user codes
-        boolean userManagerIsSplitSystemUser() {
-            return UserManager.isSplitSystemUser();
-        }
-
         boolean userManagerIsHeadlessSystemUserMode() {
             return UserManager.isHeadlessSystemUserMode();
         }
@@ -7404,48 +7397,18 @@
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
     }
 
+    // TODO (b/137101239): remove this method in follow-up CL
+    // since it's only used for split system user.
     @Override
     public void setForceEphemeralUsers(ComponentName who, boolean forceEphemeralUsers) {
-        if (!mHasFeature) {
-            return;
-        }
-        Objects.requireNonNull(who, "ComponentName is null");
-        final CallerIdentity caller = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-
-        // Allow setting this policy to true only if there is a split system user.
-        if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) {
-            throw new UnsupportedOperationException(
-                    "Cannot force ephemeral users on systems without split system user.");
-        }
-        boolean removeAllUsers = false;
-        synchronized (getLockObject()) {
-            final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
-            if (deviceOwner.forceEphemeralUsers != forceEphemeralUsers) {
-                deviceOwner.forceEphemeralUsers = forceEphemeralUsers;
-                saveSettingsLocked(caller.getUserId());
-                mUserManagerInternal.setForceEphemeralUsers(forceEphemeralUsers);
-                removeAllUsers = forceEphemeralUsers;
-            }
-        }
-        if (removeAllUsers) {
-            mInjector.binderWithCleanCallingIdentity(() -> mUserManagerInternal.removeAllUsers());
-        }
+        throw new UnsupportedOperationException("This method was used by split system user only.");
     }
 
+    // TODO (b/137101239): remove this method in follow-up CL
+    // since it's only used for split system user.
     @Override
     public boolean getForceEphemeralUsers(ComponentName who) {
-        if (!mHasFeature) {
-            return false;
-        }
-        Objects.requireNonNull(who, "ComponentName is null");
-        final CallerIdentity caller = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isDeviceOwner(caller));
-
-        synchronized (getLockObject()) {
-            final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
-            return deviceOwner.forceEphemeralUsers;
-        }
+        throw new UnsupportedOperationException("This method was used by split system user only.");
     }
 
     @Override
@@ -12733,13 +12696,6 @@
                 case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
                 case DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE:
                     return checkDeviceOwnerProvisioningPreCondition(callingUserId);
-                // TODO (b/137101239): clean up split system user codes
-                //  ACTION_PROVISION_MANAGED_USER and ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
-                //  only supported on split-user systems.
-                case DevicePolicyManager.ACTION_PROVISION_MANAGED_USER:
-                    return checkManagedUserProvisioningPreCondition(callingUserId);
-                case DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
-                    return checkManagedShareableDeviceProvisioningPreCondition(callingUserId);
             }
         }
         throw new IllegalArgumentException("Unknown provisioning action " + action);
@@ -12775,14 +12731,12 @@
             }
         }
 
-        // TODO (b/137101239): clean up split system user codes
         if (isAdb) {
             // If shell command runs after user setup completed check device status. Otherwise, OK.
             if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
                 // In non-headless system user mode, DO can be setup only if
                 // there's no non-system user
                 if (!mInjector.userManagerIsHeadlessSystemUserMode()
-                        && !mInjector.userManagerIsSplitSystemUser()
                         && mUserManager.getUserCount() > 1) {
                     return CODE_NONSYSTEM_USER_EXISTS;
                 }
@@ -12801,16 +12755,13 @@
             }
             return CODE_OK;
         } else {
-            if (!mInjector.userManagerIsSplitSystemUser()) {
-                // In non-split user mode, DO has to be user 0
-                if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
-                    return CODE_NOT_SYSTEM_USER;
-                }
-                // Only provision DO before setup wizard completes
-                // TODO (b/171423186): implement deferred DO setup for headless system user mode
-                if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
-                    return CODE_USER_SETUP_COMPLETED;
-                }
+            // DO has to be user 0
+            if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
+                return CODE_NOT_SYSTEM_USER;
+            }
+            // Only provision DO before setup wizard completes
+            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+                return CODE_USER_SETUP_COMPLETED;
             }
             return CODE_OK;
         }
@@ -12831,17 +12782,11 @@
         }
     }
 
-    // TODO (b/137101239): clean up split system user codes
     private int checkManagedProfileProvisioningPreCondition(String packageName,
             @UserIdInt int callingUserId) {
         if (!hasFeatureManagedUsers()) {
             return CODE_MANAGED_USERS_NOT_SUPPORTED;
         }
-        if (callingUserId == UserHandle.USER_SYSTEM
-                && mInjector.userManagerIsSplitSystemUser()) {
-            // Managed-profiles cannot be setup on the system user.
-            return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
-        }
         if (getProfileOwnerAsUser(callingUserId) != null) {
             // Managed user cannot have a managed profile.
             return CODE_USER_HAS_PROFILE_OWNER;
@@ -12917,37 +12862,6 @@
         return null;
     }
 
-    // TODO (b/137101239): clean up split system user codes
-    private int checkManagedUserProvisioningPreCondition(int callingUserId) {
-        if (!hasFeatureManagedUsers()) {
-            return CODE_MANAGED_USERS_NOT_SUPPORTED;
-        }
-        if (!mInjector.userManagerIsSplitSystemUser()) {
-            // ACTION_PROVISION_MANAGED_USER only supported on split-user systems.
-            return CODE_NOT_SYSTEM_USER_SPLIT;
-        }
-        if (callingUserId == UserHandle.USER_SYSTEM) {
-            // System user cannot be a managed user.
-            return CODE_SYSTEM_USER;
-        }
-        if (hasUserSetupCompleted(callingUserId)) {
-            return CODE_USER_SETUP_COMPLETED;
-        }
-        if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
-            return CODE_HAS_PAIRED;
-        }
-        return CODE_OK;
-    }
-
-    // TODO (b/137101239): clean up split system user codes
-    private int checkManagedShareableDeviceProvisioningPreCondition(int callingUserId) {
-        if (!mInjector.userManagerIsSplitSystemUser()) {
-            // ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE only supported on split-user systems.
-            return CODE_NOT_SYSTEM_USER_SPLIT;
-        }
-        return checkDeviceOwnerProvisioningPreCondition(callingUserId);
-    }
-
     private boolean hasFeatureManagedUsers() {
         try {
             return mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0);
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 6fabc58..dfa6083 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -210,7 +210,17 @@
     ErrorCode setUidReadTimeouts(const Control& control,
                                  const std::vector<android::os::incremental::PerUidReadTimeouts>&
                                          perUidReadTimeouts) const final {
-        return -ENOTSUP;
+        std::vector<incfs::UidReadTimeouts> timeouts;
+        timeouts.resize(perUidReadTimeouts.size());
+        for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) {
+            auto&& timeout = timeouts[i];
+            const auto& perUidTimeout = perUidReadTimeouts[i];
+            timeout.uid = perUidTimeout.uid;
+            timeout.minTimeUs = perUidTimeout.minTimeUs;
+            timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs;
+            timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
+        }
+        return incfs::setUidReadTimeouts(control, timeouts);
     }
 };
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0768bb9..918cab3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -333,6 +333,8 @@
             "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
     private static final String ADB_SERVICE_CLASS =
             "com.android.server.adb.AdbService$Lifecycle";
+    private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
+            "com.android.server.speech.SpeechRecognitionManagerService";
     private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
             "com.android.server.appprediction.AppPredictionManagerService";
     private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -357,6 +359,9 @@
             "com.android.server.ConnectivityServiceInitializer";
     private static final String IP_CONNECTIVITY_METRICS_CLASS =
             "com.android.server.connectivity.IpConnectivityMetrics";
+    private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
+            "com.android.server.media.MediaCommunicationService";
+
     private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
 
     private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -1629,12 +1634,21 @@
                         "MusicRecognitionManagerService not defined by OEM or disabled by flag");
             }
 
-
             startContentCaptureService(context, t);
             startAttentionService(context, t);
             startRotationResolverService(context, t);
             startSystemCaptionsManagerService(context, t);
 
+            // System Speech Recognition Service
+            if (deviceHasConfigString(context,
+                    R.string.config_defaultOnDeviceSpeechRecognitionService)) {
+                t.traceBegin("StartSpeechRecognitionManagerService");
+                mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG, "System speech recognition is not defined by OEM");
+            }
+
             // App prediction manager service
             if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
                 t.traceBegin("StartAppPredictionService");
@@ -2511,6 +2525,10 @@
         mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        t.traceBegin("StartMediaCommunicationService");
+        mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
+        t.traceEnd();
+
         ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
                 START_BLOB_STORE_SERVICE);
 
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 743848c..2932926 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -745,7 +745,8 @@
 
     private InputDevice createInputDeviceWithVibrator(int id) {
         return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
-                null, /* hasVibrator= */ true, false, false, false /* hasSensor */);
+                null, /* hasVibrator= */ true, false, false, false /* hasSensor */,
+                false /* hasBattery */);
     }
 
     private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index aadab6e..2f36c7f 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -365,13 +365,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -415,13 +415,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -511,13 +511,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -567,13 +567,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init cicam/cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -697,13 +697,13 @@
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[0], listener, clientId0);
         assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId0[0], clientPriorities[0], 0/*niceValue*/);
         mTunerResourceManagerService.registerClientProfileInternal(
                 profiles[1], new TestResourcesReclaimListener(), clientId1);
         assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .setPriority(clientPriorities[1]);
+        mTunerResourceManagerService.updateClientPriorityInternal(
+                clientId1[0], clientPriorities[1], 0/*niceValue*/);
 
         // Init lnb resources.
         int[] lnbHandles = {1};
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index 28d313b..e71c2f5 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -294,6 +294,8 @@
 
     private InputDevice createInputDevice(int id, boolean hasVibrator) {
         return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
-                null, hasVibrator, false, false, false /* hasSensor */);
+                null, hasVibrator, false, false, false /* hasSensor */, false /* hasBattery */);
+
+
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 4984799..ea8619f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -521,6 +521,7 @@
                 WINDOWING_MODE_FULLSCREEN);
     }
 
+
     @Test
     public void testKeepsPictureInPictureLaunchModeInOptions() {
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
@@ -588,11 +589,14 @@
     }
 
     @Test
-    public void testNonEmptyLayoutInfersFreeformWithEmptySize() {
+    public void testLayoutWithGravityAndEmptySizeInfersFreeformAndRespectsCurrentSize() {
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
 
+        final Rect expectedLaunchBounds = new Rect(0, 0, 200, 100);
+
         mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+        mCurrent.mBounds.set(expectedLaunchBounds);
 
         final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
                 .setGravity(Gravity.LEFT).build();
@@ -600,6 +604,9 @@
         assertEquals(RESULT_CONTINUE,
                 new CalculateRequestBuilder().setLayout(layout).calculate());
 
+        assertEquals(expectedLaunchBounds.width(), mResult.mBounds.width());
+        assertEquals(expectedLaunchBounds.height(), mResult.mBounds.height());
+
         assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
                 WINDOWING_MODE_FREEFORM);
     }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 5b03863..472d639 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,20 +314,22 @@
     public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
 
     /**
-     * A URI representing the picture that was downloaded when a call is received.
+     * A URI representing the picture that was downloaded when a call is received or uploaded
+     * when a call is placed.
+     *
      * This is a content URI within the call log provider which can be used to open a file
      * descriptor. This could be set a short time after a call is added to the Dialer app if the
-     * download is delayed for some reason. The Dialer app will receive a callback via
+     * download/upload is delayed for some reason. The Dialer app will receive a callback via
      * {@link Call.Callback#onDetailsChanged} when this value has changed.
      *
      * Reference: RCC.20 Section 2.4.3.2
      */
-    public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
+    public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
 
-    // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture.
     /**
      * A ParcelUuid used as a token to represent a picture that was uploaded prior to the call
-     * being placed.
+     * being placed. The value of this extra should be set using the {@link android.os.ParcelUuid}
+     * obtained from the callback in {@link TelephonyManager#uploadCallComposerPicture}.
      */
     public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
 
diff --git a/telephony/java/android/telephony/TelephonyLocalConnection.java b/telephony/java/android/telephony/TelephonyLocalConnection.java
new file mode 100644
index 0000000..1cab267
--- /dev/null
+++ b/telephony/java/android/telephony/TelephonyLocalConnection.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import java.util.UUID;
+
+/**
+ * Shim used for code in frameworks/opt/telephony to be able to call code in
+ * packages/services/Telephony. A singleton instance of this class is set when the phone process
+ * is brought up.
+ * @hide
+ */
+public class TelephonyLocalConnection {
+    public interface ConnectionImpl {
+        String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid);
+    }
+    private static ConnectionImpl sInstance;
+
+    public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) {
+        checkInstance();
+        return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid);
+    }
+
+    private static void checkInstance() {
+        if (sInstance == null) {
+            throw new IllegalStateException("Connection impl is null!");
+        }
+    }
+
+    public static void setInstance(ConnectionImpl impl) {
+        sInstance = impl;
+    }
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a940c7..c6c67fe 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -753,6 +753,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="RenderEffectShaderActivity"
+                  android:label="RenderEffect/Shader"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="TextActivity"
              android:label="Text/Simple Text"
              android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
new file mode 100644
index 0000000..661d48a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RenderEffectShaderActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        LinearLayout layout = new LinearLayout(this);
+        layout.setClipChildren(false);
+        layout.setGravity(Gravity.CENTER);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+        params.bottomMargin = 100;
+
+        layout.addView(new ShaderRenderEffectView(this), params);
+
+        setContentView(layout);
+    }
+
+    public static class ShaderRenderEffectView extends View {
+
+        private final Paint mPaint;
+        private final RenderNode mRenderNode;
+
+        public ShaderRenderEffectView(Context c) {
+            super(c);
+
+            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mRenderNode = new RenderNode("blurNode");
+
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            if (changed) {
+                LinearGradient gradient = new LinearGradient(
+                        0f, 0f,
+                        0f, bottom - top,
+                        new int[]{Color.CYAN, Color.MAGENTA},
+                        null,
+                        Shader.TileMode.CLAMP
+                );
+                mRenderNode.setRenderEffect(
+                        RenderEffect.createShaderEffect(gradient)
+                );
+
+                int width = right - left;
+                int height = bottom - top;
+                mRenderNode.setPosition(0, 0, width, height);
+                Canvas canvas = mRenderNode.beginRecording(width, height);
+                mPaint.setColor(Color.BLUE);
+
+                canvas.drawRect(
+                        0,
+                        0,
+                        width,
+                        height,
+                        mPaint
+                );
+
+                mPaint.setColor(Color.RED);
+                canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+                mRenderNode.endRecording();
+            }
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRenderNode(mRenderNode);
+        }
+    }
+}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 401d87a..8b34755 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -125,21 +125,11 @@
     }
 
     /**
-     * Test rollbacks of staged installs involving only apks with bad update.
-     * Trigger rollback phase.
-     */
-    @Test
-    public void testBadApkOnly_Phase3_Crash() throws Exception {
-        // One more crash to trigger rollback
-        RollbackUtils.sendCrashBroadcast(TestApp.A, 1);
-    }
-
-    /**
      * Test rollbacks of staged installs involving only apks.
      * Confirm rollback phase.
      */
     @Test
-    public void testBadApkOnly_Phase4_VerifyRollback() throws Exception {
+    public void testBadApkOnly_Phase3_VerifyRollback() throws Exception {
         assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         InstallUtils.processUserData(TestApp.A);
 
@@ -447,8 +437,10 @@
                 Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
                 Rollback.from(TestApp.A, 0).to(TestApp.A1));
 
-        // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
-        RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
+        // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT-1 times
+        RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
+        // Sleep for a while to make sure we don't trigger rollback
+        Thread.sleep(TimeUnit.SECONDS.toMillis(30));
     }
 
     @Test
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 1d5730f..67417bd 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -153,13 +153,14 @@
         getDevice().reboot();
         runPhase("testBadApkOnly_Phase2_VerifyInstall");
 
-        // Trigger rollback and wait for reboot to happen
-        runPhase("testBadApkOnly_Phase3_Crash");
+        // Launch the app to crash to trigger rollback
+        startActivity(TESTAPP_A);
+        // Wait for reboot to happen
         waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
 
         getDevice().waitForDeviceAvailable();
 
-        runPhase("testBadApkOnly_Phase4_VerifyRollback");
+        runPhase("testBadApkOnly_Phase3_VerifyRollback");
 
         assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
         assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
@@ -304,8 +305,10 @@
         getDevice().reboot();
         // Verify apex was installed and then crash the apk
         runPhase("testRollbackApexWithApkCrashing_Phase2_Crash");
-        // Wait for crash to trigger rollback
-        waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
+        // Launch the app to crash to trigger rollback
+        startActivity(TESTAPP_A);
+        // Wait for reboot to happen
+        waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
         getDevice().waitForDeviceAvailable();
         // Verify rollback occurred due to crash of apk-in-apex
         runPhase("testRollbackApexWithApkCrashing_Phase3_VerifyRollback");
@@ -631,6 +634,12 @@
         }
     }
 
+    private void startActivity(String packageName) throws Exception {
+        String cmd = "am start -S -a android.intent.action.MAIN "
+                + "-c android.intent.category.LAUNCHER " + packageName;
+        getDevice().executeShellCommand(cmd);
+    }
+
     private void crashProcess(String processName, int numberOfCrashes) throws Exception {
         String pid = "";
         String lastPid = "invalid";