Merge "Minor improvements on DevicePolicyManagerService."
diff --git a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
index 29721c5..9e519f7 100644
--- a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
@@ -184,11 +184,11 @@
 
                 override fun startParsingPackage(
                     packageName: String,
-                    baseCodePath: String,
-                    codePath: String,
+                    baseApkPath: String,
+                    path: String,
                     manifestArray: TypedArray,
                     isCoreApp: Boolean
-                ) = ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray)
+                ) = ParsingPackageImpl(packageName, baseApkPath, path, manifestArray)
             })
 
         override fun parseImpl(file: File) =
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index 2697428..a701f86 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -21,14 +21,12 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.os.RemoteException;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.PerfTestActivity;
 import android.platform.test.annotations.Presubmit;
 import android.util.MergedConfiguration;
-import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.InsetsSourceControl;
@@ -38,6 +36,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.widget.LinearLayout;
+import android.window.ClientWindowFrames;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
@@ -125,13 +124,7 @@
     }
 
     private static class RelayoutRunner {
-        final Rect mOutFrame = new Rect();
-        final Rect mOutContentInsets = new Rect();
-        final Rect mOutVisibleInsets = new Rect();
-        final Rect mOutStableInsets = new Rect();
-        final Rect mOutBackDropFrame = new Rect();
-        final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
-                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
+        final ClientWindowFrames mOutFrames = new ClientWindowFrames();
         final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
         final InsetsState mOutInsetsState = new InsetsState();
         final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
@@ -164,11 +157,9 @@
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             while (state.keepRunning()) {
                 session.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
-                        mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
-                        mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
-                        mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
-                        mOutSurfaceControl, mOutInsetsState, mOutControls, mOutSurfaceSize,
-                        mOutBlastSurfaceControl);
+                        mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
+                        mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
+                        mOutSurfaceSize, mOutBlastSurfaceControl);
             }
         }
     }
diff --git a/apex/OWNERS b/apex/OWNERS
new file mode 100644
index 0000000..9760013
--- /dev/null
+++ b/apex/OWNERS
@@ -0,0 +1,7 @@
+# Shared module build rule owners
+per-file *.bp=hansson@google.com
+per-file *.bp=jiyong@google.com
+
+# This file, and all other OWNERS files
+per-file OWNERS=dariofreni@google.com
+per-file OWNERS=hansson@google.com
diff --git a/api/current.txt b/api/current.txt
index e678d16..723ffec5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6532,6 +6532,7 @@
     method public android.graphics.Bitmap takeScreenshot();
     method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
     field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
+    field public static final int FLAG_DONT_USE_ACCESSIBILITY = 2; // 0x2
     field public static final int ROTATION_FREEZE_0 = 0; // 0x0
     field public static final int ROTATION_FREEZE_180 = 2; // 0x2
     field public static final int ROTATION_FREEZE_270 = 3; // 0x3
@@ -31303,6 +31304,7 @@
 
   @Deprecated public static class WifiConfiguration.GroupCipher {
     field @Deprecated public static final int CCMP = 3; // 0x3
+    field @Deprecated public static final int GCMP_128 = 7; // 0x7
     field @Deprecated public static final int GCMP_256 = 5; // 0x5
     field @Deprecated public static final int SMS4 = 6; // 0x6
     field @Deprecated public static final int TKIP = 2; // 0x2
@@ -31332,6 +31334,7 @@
 
   @Deprecated public static class WifiConfiguration.PairwiseCipher {
     field @Deprecated public static final int CCMP = 2; // 0x2
+    field @Deprecated public static final int GCMP_128 = 5; // 0x5
     field @Deprecated public static final int GCMP_256 = 3; // 0x3
     field @Deprecated public static final int NONE = 0; // 0x0
     field @Deprecated public static final int SMS4 = 4; // 0x4
@@ -52845,6 +52848,7 @@
     method public final boolean isFunctionPressed();
     method public static final boolean isGamepadButton(int);
     method public final boolean isLongPress();
+    method public static final boolean isMediaSessionKey(int);
     method public final boolean isMetaPressed();
     method public static boolean isModifierKey(int);
     method public final boolean isNumLockOn();
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 806e266..c12d897 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -41,6 +41,10 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+  }
+
 }
 
 package android.media.session {
@@ -53,6 +57,10 @@
     field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000
   }
 
+  public static final class MediaSession.Token implements android.os.Parcelable {
+    method public int getUid();
+  }
+
   public final class MediaSessionManager {
     method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
     method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
diff --git a/api/system-current.txt b/api/system-current.txt
old mode 100755
new mode 100644
index 3ec3467..4134711
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -205,6 +205,7 @@
     field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
     field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
+    field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
     field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
     field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -4337,6 +4338,14 @@
     method @NonNull public android.media.HwAudioSource.Builder setAudioDeviceInfo(@NonNull android.media.AudioDeviceInfo);
   }
 
+  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
+    method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnImsRxNoticeListener(@Nullable android.media.MediaPlayer.OnImsRxNoticeListener, @Nullable android.os.Handler);
+  }
+
+  public static interface MediaPlayer.OnImsRxNoticeListener {
+    method public void onImsRxNotice(@NonNull android.media.MediaPlayer, @NonNull byte[]);
+  }
+
   public final class MediaRecorder.AudioSource {
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
@@ -7343,6 +7352,7 @@
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>);
+    method public boolean is60GHzBandSupported();
     method public boolean isApMacRandomizationSupported();
     method public boolean isConnectedMacRandomizationSupported();
     method @Deprecated public boolean isDeviceToDeviceRttSupported();
@@ -7582,6 +7592,7 @@
     field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
     field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
     field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
+    field public static final int WIFI_BAND_60_GHZ = 16; // 0x10
     field public static final int WIFI_BAND_6_GHZ = 8; // 0x8
     field public static final int WIFI_BAND_BOTH = 3; // 0x3
     field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
@@ -7821,6 +7832,8 @@
     field public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 8; // 0x8
     field public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 128; // 0x80
     field public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 16384; // 0x4000
+    field public static final int BSS_CAPABILITY_DMG_ESS = 3; // 0x3
+    field public static final int BSS_CAPABILITY_DMG_IBSS = 1; // 0x1
     field public static final int BSS_CAPABILITY_DSSS_OFDM = 8192; // 0x2000
     field public static final int BSS_CAPABILITY_ESS = 1; // 0x1
     field public static final int BSS_CAPABILITY_IBSS = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index 3de1d93..de2919b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -133,7 +133,6 @@
   public class ActivityTaskManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
     method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public String listAllStacks();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToStack(int, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 5c08704..dec4a56 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -30,9 +30,8 @@
 
 #include <binder/ProcessState.h>
 
-#include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
-#include <gui/SyncScreenCaptureListener.h>
+#include <gui/ISurfaceComposer.h>
 
 #include <ui/DisplayInfo.h>
 #include <ui/GraphicTypes.h>
@@ -182,18 +181,13 @@
     ProcessState::self()->setThreadPoolMaxThreadCount(0);
     ProcessState::self()->startThreadPool();
 
-    sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
+    ScreenCaptureResults captureResults;
+    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
     }
 
-    ScreenCaptureResults captureResults = captureListener->waitForResults();
-    if (captureResults.result != NO_ERROR) {
-        close(fd);
-        return 1;
-    }
     ui::Dataspace dataspace = captureResults.capturedDataspace;
     sp<GraphicBuffer> buffer = captureResults.buffer;
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index e6e22ba..94c2305 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -488,6 +488,8 @@
         UIInteractionFrameInfoReported ui_interaction_frame_info_reported =
             305 [(module) = "framework"];
         UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"];
+        WifiDisconnectReported wifi_disconnect_reported = 307 [(module) = "wifi"];
+        WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"];
 
         // StatsdStats tracks platform atoms with ids upto 500.
         // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -854,10 +856,10 @@
 }
 
 /**
- * Logs whether a wifi connection is successful and reasons for failure if it isn't.
+ * Logs whether a Wifi connection attempt was successful and reasons for failure if it wasn't.
  *
  * Logged from:
- *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
+ *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java
  */
 message WifiConnectionResultReported {
     enum FailureCode {
@@ -869,13 +871,159 @@
         FAILURE_DHCP = 5;
         FAILURE_NETWORK_DISCONNECTION = 6;
         FAILURE_ROAM_TIMEOUT = 7;
+        FAILURE_WRONG_PASSWORD = 8;
     }
-    // true represents a successful connection
+
+    enum Trigger {
+        UNKNOWN = 0;
+        // Connection attempt was initiated manually.
+        MANUAL = 1;
+        // Automatic reconnection to the same network as connected previously.
+        RECONNECT_SAME_NETWORK = 2;
+        // Automatic reconnection to a saved network, but not the previous one.
+        AUTOCONNECT_CONFIGURED_NETWORK = 3;
+        // Automatic first connection attempt after device boot.
+        AUTOCONNECT_BOOT = 4;
+    }
+
+    // True represents a successful connection.
     optional bool connection_result = 1;
-    // reason for the connection failure
+    // Reason for the connection failure.
     optional FailureCode failure_code = 2;
-    // scan rssi before the connection attempt
+    // Scan RSSI before the connection attempt.
     optional int32 rssi = 3;
+    // Time taken by this connection attempt.
+    optional int32 connection_attempt_duration_millis = 4;
+    // Band bucket the connected network is on.
+    optional android.net.wifi.WifiBandBucket band = 5;
+    // Authentication type.
+    optional android.net.wifi.WifiAuthType auth_type = 6;
+    // What triggered this connection attempt.
+    optional Trigger trigger = 7;
+    // Whether this network was used (successfully connected to) previously.
+    optional bool network_used = 8;
+    // Time taken from the last successful connection (or device boot if that's the first one).
+    optional int32 time_since_last_connection_seconds = 9;
+}
+
+/**
+ * Logs when a Wifi connection drops.
+ *
+ * Logged from:
+ *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java
+ */
+message WifiDisconnectReported {
+    enum FailureCode {
+        UNKNOWN = 0;
+
+        // Wifi supplicant failure reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45).
+        // See ISupplicantStaIfaceCallback.java:ReasonCode
+        UNSPECIFIED = 1;
+        PREV_AUTH_NOT_VALID = 2;
+        DEAUTH_LEAVING = 3;
+        DISASSOC_DUE_TO_INACTIVITY = 4;
+        DISASSOC_AP_BUSY = 5;
+        CLASS2_FRAME_FROM_NONAUTH_STA = 6;
+        CLASS3_FRAME_FROM_NONASSOC_STA = 7;
+        DISASSOC_STA_HAS_LEFT = 8;
+        STA_REQ_ASSOC_WITHOUT_AUTH = 9;
+        PWR_CAPABILITY_NOT_VALID = 10;
+        SUPPORTED_CHANNEL_NOT_VALID = 11;
+        BSS_TRANSITION_DISASSOC = 12;
+        INVALID_IE = 13;
+        MICHAEL_MIC_FAILURE = 14;
+        FOURWAY_HANDSHAKE_TIMEOUT = 15;
+        GROUP_KEY_UPDATE_TIMEOUT = 16;
+        IE_IN_4WAY_DIFFERS = 17;
+        GROUP_CIPHER_NOT_VALID = 18;
+        PAIRWISE_CIPHER_NOT_VALID = 19;
+        AKMP_NOT_VALID = 20;
+        UNSUPPORTED_RSN_IE_VERSION = 21;
+        INVALID_RSN_IE_CAPAB = 22;
+        IEEE_802_1X_AUTH_FAILED = 23;
+        CIPHER_SUITE_REJECTED = 24;
+        TDLS_TEARDOWN_UNREACHABLE = 25;
+        TDLS_TEARDOWN_UNSPECIFIED = 26;
+        SSP_REQUESTED_DISASSOC = 27;
+        NO_SSP_ROAMING_AGREEMENT = 28;
+        BAD_CIPHER_OR_AKM = 29;
+        NOT_AUTHORIZED_THIS_LOCATION = 30;
+        SERVICE_CHANGE_PRECLUDES_TS = 31;
+        UNSPECIFIED_QOS_REASON = 32;
+        NOT_ENOUGH_BANDWIDTH = 33;
+        DISASSOC_LOW_ACK = 34;
+        EXCEEDED_TXOP = 35;
+        STA_LEAVING = 36;
+        END_TS_BA_DLS = 37;
+        UNKNOWN_TS_BA = 38;
+        TIMEOUT = 39;
+        PEERKEY_MISMATCH = 45;
+        AUTHORIZED_ACCESS_LIMIT_REACHED = 46;
+        EXTERNAL_SERVICE_REQUIREMENTS = 47;
+        INVALID_FT_ACTION_FRAME_COUNT = 48;
+        INVALID_PMKID = 49;
+        INVALID_MDE = 50;
+        INVALID_FTE = 51;
+        MESH_PEERING_CANCELLED = 52;
+        MESH_MAX_PEERS = 53;
+        MESH_CONFIG_POLICY_VIOLATION = 54;
+        MESH_CLOSE_RCVD = 55;
+        MESH_MAX_RETRIES = 56;
+        MESH_CONFIRM_TIMEOUT = 57;
+        MESH_INVALID_GTK = 58;
+        MESH_INCONSISTENT_PARAMS = 59;
+        MESH_INVALID_SECURITY_CAP = 60;
+        MESH_PATH_ERROR_NO_PROXY_INFO = 61;
+        MESH_PATH_ERROR_NO_FORWARDING_INFO = 62;
+        MESH_PATH_ERROR_DEST_UNREACHABLE = 63;
+        MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS = 64;
+        MESH_CHANNEL_SWITCH_REGULATORY_REQ = 65;
+        MESH_CHANNEL_SWITCH_UNSPECIFIED = 66;
+
+        // ClientModeImpl error codes
+        // Defined in /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java
+        IFACE_DESTROYED = 10000;
+        WIFI_DISABLED = 10001;
+        SUPPLICANT_DISCONNECTED = 10002;
+        CONNECTING_WATCHDOG_TIMER = 10003;
+        ROAM_WATCHDOG_TIMER = 10004;
+    }
+
+    // How long the session lasted from successful connection to disconnect.
+    optional int32 connected_duration_seconds = 1;
+
+    // Reason for the disconnect.
+    optional FailureCode failure_code = 2;
+
+    // Band bucket the connected network was on.
+    optional android.net.wifi.WifiBandBucket band = 3;
+
+    // Authentication type.
+    optional android.net.wifi.WifiAuthType auth_type = 4;
+
+    // Last seen RSSI before the disconnect.
+    optional int32 last_rssi = 5;
+
+    // Last seen link speed before the disconnect.
+    optional int32 last_link_speed = 6;
+}
+
+/**
+ * Logs when Wifi connection is established or dropped.
+ *
+ * Logged from:
+ *   frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java
+ */
+message WifiConnectionStateChanged {
+    optional bool is_connected = 1;
+
+    // Band bucket the connected network was on.
+    // Filled for both connected and disconnected cases.
+    optional android.net.wifi.WifiBandBucket band = 2;
+
+    // Authentication type.
+    // Filled for both connected and disconnected cases.
+    optional android.net.wifi.WifiAuthType auth_type = 3;
 }
 
 /**
@@ -11239,4 +11387,4 @@
 
     // List of leasees of this Blob
     optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES];
-}
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 46c3770..8334b6b 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -315,6 +315,7 @@
 }
 
 int StatsPullerManager::ForceClearPullerCache() {
+    std::lock_guard<std::mutex> _l(mLock);
     int totalCleared = 0;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
         totalCleared += pulledAtom.second->ForceClearCache();
@@ -323,6 +324,7 @@
 }
 
 int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+    std::lock_guard<std::mutex> _l(mLock);
     int totalCleared = 0;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
         totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl
index 29260e9..45a0e87 100644
--- a/core/java/android/app/ActivityManager.aidl
+++ b/core/java/android/app/ActivityManager.aidl
@@ -24,8 +24,6 @@
 parcelable ActivityManager.RunningServiceInfo;
 parcelable ActivityManager.RunningTaskInfo;
 /** @hide */
-parcelable ActivityManager.StackInfo;
-/** @hide */
 parcelable ActivityManager.TaskThumbnail;
 /** @hide */
 parcelable ActivityManager.TaskSnapshot;
\ No newline at end of file
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a88c6a8..5aecb61 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -76,7 +76,6 @@
 import android.util.Singleton;
 import android.util.Size;
 import android.view.Surface;
-import android.window.WindowContainerToken;
 
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.procstats.ProcessStats;
@@ -2815,160 +2814,6 @@
     }
 
     /**
-     * Information you can retrieve about an ActivityStack in the system.
-     * @hide
-     */
-    public static class StackInfo implements Parcelable {
-        @UnsupportedAppUsage
-        public int stackId;
-        @UnsupportedAppUsage
-        public Rect bounds = new Rect();
-        @UnsupportedAppUsage
-        public int[] taskIds;
-        @UnsupportedAppUsage
-        public String[] taskNames;
-        @UnsupportedAppUsage
-        public Rect[] taskBounds;
-        @UnsupportedAppUsage
-        public int[] taskUserIds;
-        @UnsupportedAppUsage
-        public ComponentName topActivity;
-        @UnsupportedAppUsage
-        public int displayId;
-        @UnsupportedAppUsage
-        public int userId;
-        @UnsupportedAppUsage
-        public boolean visible;
-        // Index of the stack in the display's stack list, can be used for comparison of stack order
-        // TODO: Can be removed since no one is using it.
-        @UnsupportedAppUsage
-        @Deprecated
-        public int position;
-        public WindowContainerToken stackToken;
-        /**
-         * The full configuration the stack is currently running in.
-         * @hide
-         */
-        final public Configuration configuration = new Configuration();
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(stackId);
-            dest.writeInt(bounds.left);
-            dest.writeInt(bounds.top);
-            dest.writeInt(bounds.right);
-            dest.writeInt(bounds.bottom);
-            dest.writeIntArray(taskIds);
-            dest.writeStringArray(taskNames);
-            final int boundsCount = taskBounds == null ? 0 : taskBounds.length;
-            dest.writeInt(boundsCount);
-            for (int i = 0; i < boundsCount; i++) {
-                dest.writeInt(taskBounds[i].left);
-                dest.writeInt(taskBounds[i].top);
-                dest.writeInt(taskBounds[i].right);
-                dest.writeInt(taskBounds[i].bottom);
-            }
-            dest.writeIntArray(taskUserIds);
-            dest.writeInt(displayId);
-            dest.writeInt(userId);
-            dest.writeInt(visible ? 1 : 0);
-            dest.writeInt(position);
-            stackToken.writeToParcel(dest, 0);
-            if (topActivity != null) {
-                dest.writeInt(1);
-                topActivity.writeToParcel(dest, 0);
-            } else {
-                dest.writeInt(0);
-            }
-            configuration.writeToParcel(dest, flags);
-        }
-
-        public void readFromParcel(Parcel source) {
-            stackId = source.readInt();
-            bounds = new Rect(
-                    source.readInt(), source.readInt(), source.readInt(), source.readInt());
-            taskIds = source.createIntArray();
-            taskNames = source.createStringArray();
-            final int boundsCount = source.readInt();
-            if (boundsCount > 0) {
-                taskBounds = new Rect[boundsCount];
-                for (int i = 0; i < boundsCount; i++) {
-                    taskBounds[i] = new Rect();
-                    taskBounds[i].set(
-                            source.readInt(), source.readInt(), source.readInt(), source.readInt());
-                }
-            } else {
-                taskBounds = null;
-            }
-            taskUserIds = source.createIntArray();
-            displayId = source.readInt();
-            userId = source.readInt();
-            visible = source.readInt() > 0;
-            position = source.readInt();
-            stackToken = WindowContainerToken.CREATOR.createFromParcel(source);
-            if (source.readInt() > 0) {
-                topActivity = ComponentName.readFromParcel(source);
-            }
-            configuration.readFromParcel(source);
-        }
-
-        public static final @android.annotation.NonNull Creator<StackInfo> CREATOR = new Creator<StackInfo>() {
-            @Override
-            public StackInfo createFromParcel(Parcel source) {
-                return new StackInfo(source);
-            }
-            @Override
-            public StackInfo[] newArray(int size) {
-                return new StackInfo[size];
-            }
-        };
-
-        public StackInfo() {
-        }
-
-        private StackInfo(Parcel source) {
-            readFromParcel(source);
-        }
-
-        @UnsupportedAppUsage
-        public String toString(String prefix) {
-            StringBuilder sb = new StringBuilder(256);
-            sb.append(prefix); sb.append("Stack id="); sb.append(stackId);
-                    sb.append(" bounds="); sb.append(bounds.toShortString());
-                    sb.append(" displayId="); sb.append(displayId);
-                    sb.append(" userId="); sb.append(userId);
-                    sb.append("\n");
-                    sb.append(" configuration="); sb.append(configuration);
-                    sb.append("\n");
-            prefix = prefix + "  ";
-            for (int i = 0; i < taskIds.length; ++i) {
-                sb.append(prefix); sb.append("taskId="); sb.append(taskIds[i]);
-                        sb.append(": "); sb.append(taskNames[i]);
-                        if (taskBounds != null) {
-                            sb.append(" bounds="); sb.append(taskBounds[i].toShortString());
-                        }
-                        sb.append(" userId=").append(taskUserIds[i]);
-                        sb.append(" visible=").append(visible);
-                        if (topActivity != null) {
-                            sb.append(" topActivity=").append(topActivity);
-                        }
-                        sb.append("\n");
-            }
-            return sb.toString();
-        }
-
-        @Override
-        public String toString() {
-            return toString("");
-        }
-    }
-
-    /**
      * @hide
      */
     @RequiresPermission(anyOf={Manifest.permission.CLEAR_APP_USER_DATA,
diff --git a/core/java/android/app/ActivityTaskManager.aidl b/core/java/android/app/ActivityTaskManager.aidl
new file mode 100644
index 0000000..a12bcd5
--- /dev/null
+++ b/core/java/android/app/ActivityTaskManager.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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 android.app;
+
+/** @hide */
+parcelable ActivityTaskManager.RootTaskInfo;
\ No newline at end of file
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4283d7a..3e4d5ee 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -29,6 +29,8 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.DisplayMetrics;
@@ -391,27 +393,6 @@
     }
 
     /**
-     * List all activity stacks information.
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public String listAllStacks() {
-        final List<ActivityManager.StackInfo> stacks;
-        try {
-            stacks = getService().getAllStackInfos();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        final StringBuilder sb = new StringBuilder();
-        if (stacks != null) {
-            for (ActivityManager.StackInfo info : stacks) {
-                sb.append(info).append("\n");
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
      * Clears launch params for the given package.
      * @param packageNames the names of the packages of which the launch params are to be cleared
      */
@@ -468,4 +449,96 @@
         final Configuration config = context.getResources().getConfiguration();
         return currentUiModeSupportsErrorDialogs(config);
     }
+
+    /**
+     * Information you can retrieve about a root task in the system.
+     * @hide
+     */
+    public static class RootTaskInfo extends TaskInfo implements Parcelable {
+        // TODO(b/148895075): Move some of the fields to TaskInfo.
+        public Rect bounds = new Rect();
+        public int[] childTaskIds;
+        public String[] childTaskNames;
+        public Rect[] childTaskBounds;
+        public int[] childTaskUserIds;
+        public boolean visible;
+        // Index of the stack in the display's stack list, can be used for comparison of stack order
+        public int position;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeTypedObject(bounds, flags);
+            dest.writeIntArray(childTaskIds);
+            dest.writeStringArray(childTaskNames);
+            dest.writeTypedArray(childTaskBounds, flags);
+            dest.writeIntArray(childTaskUserIds);
+            dest.writeInt(visible ? 1 : 0);
+            dest.writeInt(position);
+            super.writeToParcel(dest, flags);
+        }
+
+        @Override
+        void readFromParcel(Parcel source) {
+            bounds = source.readTypedObject(Rect.CREATOR);
+            childTaskIds = source.createIntArray();
+            childTaskNames = source.createStringArray();
+            childTaskBounds = source.createTypedArray(Rect.CREATOR);
+            childTaskUserIds = source.createIntArray();
+            visible = source.readInt() > 0;
+            position = source.readInt();
+            super.readFromParcel(source);
+        }
+
+        public static final @NonNull Creator<RootTaskInfo> CREATOR = new Creator<>() {
+            @Override
+            public RootTaskInfo createFromParcel(Parcel source) {
+                return new RootTaskInfo(source);
+            }
+
+            @Override
+            public RootTaskInfo[] newArray(int size) {
+                return new RootTaskInfo[size];
+            }
+        };
+
+        public RootTaskInfo() {
+        }
+
+        private RootTaskInfo(Parcel source) {
+            readFromParcel(source);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(256);
+            sb.append("RootTask id="); sb.append(taskId);
+            sb.append(" bounds="); sb.append(bounds.toShortString());
+            sb.append(" displayId="); sb.append(displayId);
+            sb.append(" userId="); sb.append(userId);
+            sb.append("\n");
+
+            sb.append(" configuration="); sb.append(configuration);
+            sb.append("\n");
+
+            for (int i = 0; i < childTaskIds.length; ++i) {
+                sb.append("  taskId="); sb.append(childTaskIds[i]);
+                sb.append(": "); sb.append(childTaskNames[i]);
+                if (childTaskBounds != null) {
+                    sb.append(" bounds="); sb.append(childTaskBounds[i].toShortString());
+                }
+                sb.append(" userId=").append(childTaskUserIds[i]);
+                sb.append(" visible=").append(visible);
+                if (topActivity != null) {
+                    sb.append(" topActivity=").append(topActivity);
+                }
+                sb.append("\n");
+            }
+            return sb.toString();
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index cd9e02d..45b25a3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
 import android.app.ContentProviderHolder;
@@ -99,7 +100,6 @@
     void unregisterUidObserver(in IUidObserver observer);
     boolean isUidActive(int uid, String callingPackage);
     int getUidProcessState(int uid, in String callingPackage);
-    boolean setSchedPolicyCgroup(int tid, int group);
     // =============== End of transactions used on native side as well ============================
 
     // Special low-level communication with activity manager.
@@ -449,12 +449,11 @@
     @UnsupportedAppUsage
     void hang(in IBinder who, boolean allowRestart);
 
-    @UnsupportedAppUsage
-    List<ActivityManager.StackInfo> getAllStackInfos();
+    List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos();
     @UnsupportedAppUsage
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
     void setFocusedStack(int stackId);
-    ActivityManager.StackInfo getFocusedStackInfo();
+    ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     @UnsupportedAppUsage
     void restart();
     void performIdleMaintenance();
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 72a3637d..f3c7fe94 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.ApplicationErrorReport;
 import android.app.ContentProviderHolder;
 import android.app.GrantedUriPermission;
@@ -186,7 +187,7 @@
             in AssistStructure structure, in AssistContent content, in Uri referrer);
 
     void setFocusedStack(int stackId);
-    ActivityManager.StackInfo getFocusedStackInfo();
+    ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     Rect getTaskBounds(int taskId);
 
     void cancelRecentsAnimation(boolean restoreHomeStackPosition);
@@ -260,11 +261,10 @@
     /** Removes stack of the activity types from the system. */
     void removeStacksWithActivityTypes(in int[] activityTypes);
 
-    List<ActivityManager.StackInfo> getAllStackInfos();
-    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType);
-    List<ActivityManager.StackInfo> getAllStackInfosOnDisplay(int displayId);
-    ActivityManager.StackInfo getStackInfoOnDisplay(int windowingMode, int activityType,
-            int displayId);
+    List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos();
+    ActivityTaskManager.RootTaskInfo getRootTaskInfo(int windowingMode, int activityType);
+    List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId);
+    ActivityTaskManager.RootTaskInfo getRootTaskInfoOnDisplay(int windowingMode, int activityType, int displayId);
 
     /**
      * Informs ActivityTaskManagerService that the keyguard is showing.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 2ef147b..f2a9daa 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -97,7 +97,8 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES})
+    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES,
+            UiAutomation.FLAG_DONT_USE_ACCESSIBILITY})
     public @interface UiAutomationFlags {};
 
 
@@ -2170,7 +2171,8 @@
      * </p>
      *
      * @param flags The flags to be passed to the UiAutomation, for example
-     *        {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}.
+     *        {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES},
+     *        {@link UiAutomation#FLAG_DONT_USE_ACCESSIBILITY}.
      *
      * @return The UI automation instance.
      *
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index f3f00e5..4718cf1 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -36,7 +36,8 @@
     private static final String TAG = "TaskInfo";
 
     /**
-     * The id of the user the task was running as.
+     * The id of the user the task was running as if this is a leaf task. The id of the current
+     * running user of the system otherwise.
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 109205f..ab997e9 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -56,6 +56,7 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import libcore.io.IoUtils;
@@ -142,13 +143,22 @@
     }
 
     /**
-     * UiAutomation supresses accessibility services by default. This flag specifies that
+     * UiAutomation suppresses accessibility services by default. This flag specifies that
      * existing accessibility services should continue to run, and that new ones may start.
      * This flag is set when obtaining the UiAutomation from
      * {@link Instrumentation#getUiAutomation(int)}.
      */
     public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 0x00000001;
 
+    /**
+     * UiAutomation uses the accessibility subsystem by default. This flag provides an option to
+     * eliminate the overhead of engaging the accessibility subsystem for tests that do not need to
+     * interact with the user interface. Setting this flag disables methods that rely on
+     * accessibility. This flag is set when obtaining the UiAutomation from
+     * {@link Instrumentation#getUiAutomation(int)}.
+     */
+    public static final int FLAG_DONT_USE_ACCESSIBILITY = 0x00000002;
+
     private final Object mLock = new Object();
 
     private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>();
@@ -267,11 +277,14 @@
     /**
      * Connects this UiAutomation to the accessibility introspection APIs.
      *
-     * @param flags Any flags to apply to the automation as it gets connected
+     * @param flags Any flags to apply to the automation as it gets connected while
+     *              {@link UiAutomation#FLAG_DONT_USE_ACCESSIBILITY} would keep the
+     *              connection disconnected and not to register UiAutomation service.
      * @param timeoutMillis The wait timeout in milliseconds
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is already
+     *            established.
      * @throws TimeoutException If not connected within the timeout
-     *
      * @hide
      */
     public void connectWithTimeout(int flags, long timeoutMillis) throws TimeoutException {
@@ -292,6 +305,12 @@
             // Calling out without a lock held.
             mUiAutomationConnection.connect(mClient, flags);
             mFlags = flags;
+            // If UiAutomation is not allowed to use the accessibility subsystem, the
+            // connection state should keep disconnected and not to start the client connection.
+            if (!useAccessibility()) {
+                mConnectionState = ConnectionState.DISCONNECTED;
+                return;
+            }
         } catch (RemoteException re) {
             throw new RuntimeException("Error while connecting " + this, re);
         }
@@ -340,7 +359,7 @@
                 throw new IllegalStateException(
                         "Cannot call disconnect() while connecting " + this);
             }
-            if (mConnectionState == ConnectionState.DISCONNECTED) {
+            if (useAccessibility() && mConnectionState == ConnectionState.DISCONNECTED) {
                 return;
             }
             mConnectionState = ConnectionState.DISCONNECTED;
@@ -354,8 +373,10 @@
         } catch (RemoteException re) {
             throw new RuntimeException("Error while disconnecting " + this, re);
         } finally {
-            mRemoteCallbackThread.quit();
-            mRemoteCallbackThread = null;
+            if (mRemoteCallbackThread != null) {
+                mRemoteCallbackThread.quit();
+                mRemoteCallbackThread = null;
+            }
         }
     }
 
@@ -389,9 +410,13 @@
      * The callbacks are delivered on the main application thread.
      *
      * @param listener The callback.
+     *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      */
     public void setOnAccessibilityEventListener(OnAccessibilityEventListener listener) {
         synchronized (mLock) {
+            throwIfNotConnectedLocked();
             mOnAccessibilityEventListener = listener;
         }
     }
@@ -423,9 +448,6 @@
      * @see #dropShellPermissionIdentity()
      */
     public void adoptShellPermissionIdentity() {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             // Calling out without a lock held.
             mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), null);
@@ -451,9 +473,6 @@
      * @see #dropShellPermissionIdentity()
      */
     public void adoptShellPermissionIdentity(@Nullable String... permissions) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             // Calling out without a lock held.
             mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), permissions);
@@ -470,9 +489,6 @@
      * @see #adoptShellPermissionIdentity()
      */
     public void dropShellPermissionIdentity() {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             // Calling out without a lock held.
             mUiAutomationConnection.dropShellPermissionIdentity();
@@ -489,6 +505,8 @@
      * @param action The action to perform.
      * @return Whether the action was successfully performed.
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK
      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME
      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
@@ -523,13 +541,18 @@
      * </p>
      *
      * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
-     *         {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
+     *              {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
      * @return The node info of the focused view or null.
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      * @see AccessibilityNodeInfo#FOCUS_INPUT
      * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
      */
     public AccessibilityNodeInfo findFocus(int focus) {
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+        }
         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
                 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
     }
@@ -541,6 +564,8 @@
      *
      * @return The accessibility service info.
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      * @see AccessibilityServiceInfo
      */
     public final AccessibilityServiceInfo getServiceInfo() {
@@ -567,6 +592,8 @@
      *
      * @param info The info.
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      * @see AccessibilityServiceInfo
      */
     public final void setServiceInfo(AccessibilityServiceInfo info) {
@@ -602,6 +629,8 @@
      * </p>
      *
      * @return The windows if there are windows such, otherwise an empty list.
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      */
     public List<AccessibilityWindowInfo> getWindows() {
         final int connectionId;
@@ -630,6 +659,8 @@
      *
      * @return The windows of all displays if there are windows and the service is can retrieve
      *         them, otherwise an empty list. The key of SparseArray is display ID.
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      */
     @NonNull
     public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
@@ -647,6 +678,8 @@
      * Gets the root {@link AccessibilityNodeInfo} in the active window.
      *
      * @return The root info.
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      */
     public AccessibilityNodeInfo getRootInActiveWindow() {
         final int connectionId;
@@ -664,14 +697,12 @@
      * <p>
      * <strong>Note:</strong> It is caller's responsibility to recycle the event.
      * </p>
+     *
      * @param event The event to inject.
      * @param sync Whether to inject the event synchronously.
      * @return Whether event injection succeeded.
      */
     public boolean injectInputEvent(InputEvent event, boolean sync) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             if (DEBUG) {
                 Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync);
@@ -692,9 +723,6 @@
      */
     @TestApi
     public void syncInputTransactions() {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             // Calling out without a lock held.
             mUiAutomationConnection.syncInputTransactions();
@@ -720,9 +748,6 @@
      * @see #ROTATION_UNFREEZE
      */
     public boolean setRotation(int rotation) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         switch (rotation) {
             case ROTATION_FREEZE_0:
             case ROTATION_FREEZE_90:
@@ -752,11 +777,14 @@
      * <p>
      * <strong>Note:</strong> It is caller's responsibility to recycle the returned event.
      * </p>
+     *
      * @param command The command to execute.
      * @param filter Filter that recognizes the expected event.
      * @param timeoutMillis The wait timeout in milliseconds.
      *
      * @throws TimeoutException If the expected event is not received within the timeout.
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      */
     public AccessibilityEvent executeAndWaitForEvent(Runnable command,
             AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
@@ -845,6 +873,8 @@
      *
      * @throws TimeoutException If no idle state was detected within
      *            <code>globalTimeoutMillis.</code>
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      */
     public void waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis)
             throws TimeoutException {
@@ -874,9 +904,9 @@
                     return;
                 }
                 try {
-                     mLock.wait(remainingIdleTimeMillis);
+                    mLock.wait(remainingIdleTimeMillis);
                 } catch (InterruptedException ie) {
-                     /* ignore */
+                    /* ignore */
                 }
             }
         }
@@ -888,9 +918,6 @@
      * @return The screenshot bitmap on success, null otherwise.
      */
     public Bitmap takeScreenshot() {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         Display display = DisplayManagerGlobal.getInstance()
                 .getRealDisplay(Display.DEFAULT_DISPLAY);
         Point displaySize = new Point();
@@ -927,9 +954,6 @@
      * @see ActivityManager#isUserAMonkey()
      */
     public void setRunAsMonkey(boolean enable) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             ActivityManager.getService().setUserIsMonkey(enable);
         } catch (RemoteException re) {
@@ -946,6 +970,8 @@
      * @return Whether the window is present and its frame statistics
      *         were cleared.
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      * @see android.view.WindowContentFrameStats
      * @see #getWindowContentFrameStats(int)
      * @see #getWindows()
@@ -991,6 +1017,8 @@
      * @param windowId The window id.
      * @return The window frame statistics, or null if the window is not present.
      *
+     * @throws IllegalStateException If the connection to the accessibility subsystem is not
+     *            established.
      * @see android.view.WindowContentFrameStats
      * @see #clearWindowContentFrameStats(int)
      * @see #getWindows()
@@ -1022,9 +1050,6 @@
      * @see android.R.styleable#WindowAnimation
      */
     public void clearWindowAnimationFrameStats() {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             if (DEBUG) {
                 Log.i(LOG_TAG, "Clearing window animation frame stats");
@@ -1064,9 +1089,6 @@
      * @see android.R.styleable#WindowAnimation
      */
     public WindowAnimationFrameStats getWindowAnimationFrameStats() {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             if (DEBUG) {
                 Log.i(LOG_TAG, "Getting window animation frame stats");
@@ -1081,6 +1103,7 @@
 
     /**
      * Grants a runtime permission to a package.
+     *
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
      * @throws SecurityException if unable to grant the permission.
@@ -1104,15 +1127,13 @@
 
     /**
      * Grants a runtime permission to a package for a user.
+     *
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
      * @throws SecurityException if unable to grant the permission.
      */
     public void grantRuntimePermissionAsUser(String packageName, String permission,
             UserHandle userHandle) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             if (DEBUG) {
                 Log.i(LOG_TAG, "Granting runtime permission");
@@ -1127,6 +1148,7 @@
 
     /**
      * Revokes a runtime permission from a package.
+     *
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
      * @throws SecurityException if unable to revoke the permission.
@@ -1150,15 +1172,13 @@
 
     /**
      * Revokes a runtime permission from a package.
+     *
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
      * @throws SecurityException if unable to revoke the permission.
      */
     public void revokeRuntimePermissionAsUser(String packageName, String permission,
             UserHandle userHandle) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         try {
             if (DEBUG) {
                 Log.i(LOG_TAG, "Revoking runtime permission");
@@ -1186,9 +1206,6 @@
      * @see #adoptShellPermissionIdentity()
      */
     public ParcelFileDescriptor executeShellCommand(String command) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         warnIfBetterCommand(command);
 
         ParcelFileDescriptor source = null;
@@ -1229,9 +1246,6 @@
      */
     @TestApi
     public ParcelFileDescriptor[] executeShellCommandRw(String command) {
-        synchronized (mLock) {
-            throwIfNotConnectedLocked();
-        }
         warnIfBetterCommand(command);
 
         ParcelFileDescriptor source_read = null;
@@ -1276,15 +1290,21 @@
         return stringBuilder.toString();
     }
 
+    @GuardedBy("mLock")
     private void throwIfConnectedLocked() {
         if (mConnectionState == ConnectionState.CONNECTED) {
             throw new IllegalStateException("UiAutomation connected, " + this);
         }
     }
 
+    @GuardedBy("mLock")
     private void throwIfNotConnectedLocked() {
         if (mConnectionState != ConnectionState.CONNECTED) {
-            throw new IllegalStateException("UiAutomation not connected, " + this);
+            final String msg = useAccessibility()
+                    ? "UiAutomation not connected, "
+                    : "UiAutomation not connected: Accessibility-dependent method called with "
+                            + "FLAG_DONT_USE_ACCESSIBILITY set, ";
+            throw new IllegalStateException(msg + this);
         }
     }
 
@@ -1298,11 +1318,16 @@
         }
     }
 
+    private boolean useAccessibility() {
+        return (mFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0;
+    }
+
     private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
 
         public IAccessibilityServiceClientImpl(Looper looper, int generationId) {
             super(null, looper, new Callbacks() {
                 private final int mGenerationId = generationId;
+
                 /**
                  * True if UiAutomation doesn't interact with this client anymore.
                  * Used by methods below to stop sending notifications or changing members
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0e6a063..3522b1b 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -199,7 +199,7 @@
         public static final int NOTIFICATION_INTERRUPTION = 12;
 
         /**
-         * A Slice was pinned by the default assistant.
+         * A Slice was pinned by the default launcher or the default assistant.
          * @hide
          */
         @SystemApi
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index b434072..eec7c9c 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -343,20 +343,22 @@
      * @param permission The permission to check.
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
-     * @param attributionTag attribution tag of caller (if not self)
+     * @param callingPackageName package name tag of caller (if not self)
+     * @param callingAttributionTag attribution tag of caller (if not self)
      * @param message A message describing the reason the permission was checked
      *
      * @see #checkCallingOrSelfPermissionForPreflight(Context, String)
      */
     @PermissionResult
     public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
-            @NonNull String permission, @Nullable String attributionTag, @Nullable String message) {
-        String packageName = (Binder.getCallingPid() == Process.myPid())
-                ? context.getPackageName() : null;
-        attributionTag = (Binder.getCallingPid() == Process.myPid())
-                ? context.getAttributionTag() : attributionTag;
+            @NonNull String permission, @Nullable String callingPackageName,
+            @Nullable String callingAttributionTag, @Nullable String message) {
+        callingPackageName = (Binder.getCallingPid() == Process.myPid())
+                ? context.getPackageName() : callingPackageName;
+        callingAttributionTag = (Binder.getCallingPid() == Process.myPid())
+                ? context.getAttributionTag() : callingAttributionTag;
         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName, attributionTag, message);
+                Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
     }
 
     /**
@@ -375,15 +377,15 @@
      * app's fg/gb state) and this check will not leave a trace that permission protected
      * data was delivered. When you are about to deliver the location data to a registered
      * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
-     * String, String, String)} which will evaluate the permission access based on the current
-     * fg/bg state of the app and leave a record that the data was accessed.
+     * String, String, String, String)} which will evaluate the permission access based on the
+     * current fg/bg state of the app and leave a record that the data was accessed.
      *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
      *
-     * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String, String, String)
+     * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String, String, String, String)
      */
     @PermissionResult
     public static int checkCallingOrSelfPermissionForPreflight(@NonNull Context context,
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 542cfc2..6d92d3e 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -53,7 +53,7 @@
 /** @hide */
 public class ApkLiteParseUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     // TODO(b/135203078): Consolidate constants
     private static final int DEFAULT_MIN_SDK_VERSION = 1;
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index e450748..e573539 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -579,8 +579,8 @@
         ii.handleProfiling = i.isHandleProfiling();
         ii.functionalTest = i.isFunctionalTest();
 
-        ii.sourceDir = pkg.getBaseCodePath();
-        ii.publicSourceDir = pkg.getBaseCodePath();
+        ii.sourceDir = pkg.getBaseApkPath();
+        ii.publicSourceDir = pkg.getBaseApkPath();
         ii.splitNames = pkg.getSplitNames();
         ii.splitSourceDirs = pkg.getSplitCodePaths();
         ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 0c0dc31..ed12a17 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -145,7 +145,7 @@
     private String realPackage;
 
     @NonNull
-    protected String baseCodePath;
+    protected String mBaseApkPath;
 
     private boolean requiredForAllUsers;
     @Nullable
@@ -280,7 +280,7 @@
 
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
-    protected String codePath;
+    protected String mPath;
 
     private boolean use32BitAbi;
     private boolean visibleToInstantApps;
@@ -429,11 +429,11 @@
     private ArraySet<String> mimeGroups;
 
     @VisibleForTesting
-    public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseCodePath,
-            @NonNull String codePath, @Nullable TypedArray manifestArray) {
+    public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
+            @NonNull String path, @Nullable TypedArray manifestArray) {
         this.packageName = TextUtils.safeIntern(packageName);
-        this.baseCodePath = baseCodePath;
-        this.codePath = codePath;
+        this.mBaseApkPath = baseApkPath;
+        this.mPath = path;
 
         if (manifestArray != null) {
             versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
@@ -888,6 +888,9 @@
     public ApplicationInfo toAppInfoWithoutStateWithoutFlags() {
         ApplicationInfo appInfo = new ApplicationInfo();
 
+        // Lines that are commented below are state related and should not be assigned here.
+        // They are left in as placeholders, since there is no good backwards compatible way to
+        // separate these.
         appInfo.appComponentFactory = appComponentFactory;
         appInfo.backupAgentName = backupAgentName;
         appInfo.banner = banner;
@@ -897,13 +900,17 @@
         appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
         appInfo.compileSdkVersion = compileSdkVersion;
         appInfo.compileSdkVersionCodename = compileSdkVersionCodeName;
-//        appInfo.credentialProtectedDataDir = credentialProtectedDataDir;
-//        appInfo.dataDir = dataDir;
+//        appInfo.credentialProtectedDataDir
+        appInfo.crossProfile = isCrossProfile();
+//        appInfo.dataDir
         appInfo.descriptionRes = descriptionRes;
-//        appInfo.deviceProtectedDataDir = deviceProtectedDataDir;
+//        appInfo.deviceProtectedDataDir
         appInfo.enabled = enabled;
+//        appInfo.enabledSetting
         appInfo.fullBackupContent = fullBackupContent;
-//        appInfo.hiddenUntilInstalled = hiddenUntilInstalled;
+        // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
+//        appInfo.mHiddenApiPolicy
+//        appInfo.hiddenUntilInstalled
         appInfo.icon = (PackageParser.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
         appInfo.iconRes = iconRes;
         appInfo.roundIconRes = roundIconRes;
@@ -920,9 +927,9 @@
         if (appInfo.name != null) {
             appInfo.name = appInfo.name.trim();
         }
-//        appInfo.nativeLibraryDir = nativeLibraryDir;
-//        appInfo.nativeLibraryRootDir = nativeLibraryRootDir;
-//        appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+//        appInfo.nativeLibraryDir
+//        appInfo.nativeLibraryRootDir
+//        appInfo.nativeLibraryRootRequiresIsa
         appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
         appInfo.nonLocalizedLabel = nonLocalizedLabel;
         if (appInfo.nonLocalizedLabel != null) {
@@ -930,16 +937,17 @@
         }
         appInfo.packageName = packageName;
         appInfo.permission = permission;
-//        appInfo.primaryCpuAbi = primaryCpuAbi;
+//        appInfo.primaryCpuAbi
         appInfo.processName = getProcessName();
         appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
-//        appInfo.secondaryCpuAbi = secondaryCpuAbi;
-//        appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
-//        appInfo.seInfo = seInfo;
-//        appInfo.seInfoUser = seInfoUser;
-//        appInfo.sharedLibraryFiles = usesLibraryFiles.isEmpty()
-//                ? null : usesLibraryFiles.toArray(new String[0]);
-//        appInfo.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+//        appInfo.resourceDirs
+//        appInfo.secondaryCpuAbi
+//        appInfo.secondaryNativeLibraryDir
+//        appInfo.seInfo
+//        appInfo.seInfoUser
+//        appInfo.sharedLibraryFiles
+//        appInfo.sharedLibraryInfos
+//        appInfo.showUserIcon
         appInfo.splitClassLoaderNames = splitClassLoaderNames;
         appInfo.splitDependencies = splitDependencies;
         appInfo.splitNames = splitNames;
@@ -948,29 +956,19 @@
         appInfo.targetSdkVersion = targetSdkVersion;
         appInfo.taskAffinity = taskAffinity;
         appInfo.theme = theme;
-//        appInfo.uid = uid;
+//        appInfo.uid
         appInfo.uiOptions = uiOptions;
         appInfo.volumeUuid = volumeUuid;
         appInfo.zygotePreloadName = zygotePreloadName;
-        appInfo.crossProfile = isCrossProfile();
         appInfo.setGwpAsanMode(gwpAsanMode);
-        appInfo.setBaseCodePath(baseCodePath);
-        appInfo.setBaseResourcePath(baseCodePath);
-        appInfo.setCodePath(codePath);
-        appInfo.setResourcePath(codePath);
+        appInfo.setBaseCodePath(mBaseApkPath);
+        appInfo.setBaseResourcePath(mBaseApkPath);
+        appInfo.setCodePath(mPath);
+        appInfo.setResourcePath(mPath);
         appInfo.setSplitCodePaths(splitCodePaths);
         appInfo.setSplitResourcePaths(splitCodePaths);
         appInfo.setVersionCode(PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode));
 
-        // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo.
-//        appInfo.showUserIcon = pkg.getShowUserIcon();
-        // TODO(b/135203078): Unused?
-//        appInfo.resourceDirs = pkg.getResourceDirs();
-        // TODO(b/135203078): Unused?
-//        appInfo.enabledSetting = pkg.getEnabledSetting();
-        // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
-//        appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy();
-
         return appInfo;
     }
 
@@ -995,7 +993,7 @@
         dest.writeString(this.compileSdkVersionCodeName);
         sForInternedString.parcel(this.packageName, dest, flags);
         dest.writeString(this.realPackage);
-        dest.writeString(this.baseCodePath);
+        dest.writeString(this.mBaseApkPath);
         dest.writeBoolean(this.requiredForAllUsers);
         dest.writeString(this.restrictedAccountType);
         dest.writeString(this.requiredAccountType);
@@ -1050,7 +1048,7 @@
         dest.writeBundle(this.metaData);
         sForInternedString.parcel(this.volumeUuid, dest, flags);
         dest.writeParcelable(this.signingDetails, flags);
-        dest.writeString(this.codePath);
+        dest.writeString(this.mPath);
         dest.writeBoolean(this.use32BitAbi);
         dest.writeBoolean(this.visibleToInstantApps);
         dest.writeBoolean(this.forceQueryable);
@@ -1159,7 +1157,7 @@
         this.compileSdkVersionCodeName = in.readString();
         this.packageName = sForInternedString.unparcel(in);
         this.realPackage = in.readString();
-        this.baseCodePath = in.readString();
+        this.mBaseApkPath = in.readString();
         this.requiredForAllUsers = in.readBoolean();
         this.restrictedAccountType = in.readString();
         this.requiredAccountType = in.readString();
@@ -1214,7 +1212,7 @@
         this.metaData = in.readBundle(boot);
         this.volumeUuid = sForInternedString.unparcel(in);
         this.signingDetails = in.readParcelable(boot);
-        this.codePath = in.readString();
+        this.mPath = in.readString();
         this.use32BitAbi = in.readBoolean();
         this.visibleToInstantApps = in.readBoolean();
         this.forceQueryable = in.readBoolean();
@@ -1363,8 +1361,8 @@
 
     @NonNull
     @Override
-    public String getBaseCodePath() {
-        return baseCodePath;
+    public String getBaseApkPath() {
+        return mBaseApkPath;
     }
 
     @Override
@@ -1649,8 +1647,8 @@
 
     @NonNull
     @Override
-    public String getCodePath() {
-        return codePath;
+    public String getPath() {
+        return mPath;
     }
 
     @Override
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 7e0fe7d..dbd15f5 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -548,7 +548,7 @@
     String getPackageName();
 
     /** Path of base APK */
-    String getBaseCodePath();
+    String getBaseApkPath();
 
     /**
      * Path where this package was found on disk. For monolithic packages
@@ -556,7 +556,7 @@
      * path to the cluster directory.
      */
     @NonNull
-    String getCodePath();
+    String getPath();
 
     /**
      * @see ApplicationInfo#compatibleWidthLimitDp
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index d9857e0..bce75cd 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -127,7 +127,7 @@
  */
 public class ParsingPackageUtils {
 
-    public static final String TAG = ParsingUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     /**
      * @see #parseDefault(ParseInput, File, int, boolean)
@@ -162,10 +162,10 @@
             @Override
             public ParsingPackage startParsingPackage(
                     @NonNull String packageName,
-                    @NonNull String baseCodePath,
-                    @NonNull String codePath,
+                    @NonNull String baseApkPath,
+                    @NonNull String path,
                     @NonNull TypedArray manifestArray, boolean isCoreApp) {
-                return new ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray);
+                return new ParsingPackageImpl(packageName, baseApkPath, path, manifestArray);
             }
         });
         try {
@@ -739,7 +739,6 @@
             String tagName = parser.getName();
             final ParseResult result;
 
-            // TODO(b/135203078): Convert to instance methods to share variables
             // <application> has special logic, so it's handled outside the general method
             if (PackageParser.TAG_APPLICATION.equals(tagName)) {
                 if (foundApp) {
@@ -1214,9 +1213,9 @@
                 features = ArrayUtils.add(features, featureInfo);
             } else {
                 Slog.w(TAG,
-                        "Unknown element under <feature-group>: " + innerTagName +
-                                " at " + pkg.getBaseCodePath() + " " +
-                                parser.getPositionDescription());
+                        "Unknown element under <feature-group>: " + innerTagName
+                                + " at " + pkg.getBaseApkPath() + " "
+                                + parser.getPositionDescription());
             }
         }
 
@@ -1719,10 +1718,6 @@
                 pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
             }
 
-            // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a
-            //  util or just have PackageImpl return true if either flag is set
-            pkg.setProfileableByShell(pkg.isProfileableByShell());
-
             if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
                 pkg.setResizeableActivity(sa.getBoolean(
                         R.styleable.AndroidManifestApplication_resizeableActivity, true));
@@ -2424,7 +2419,7 @@
                     R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
             if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) {
                 String message = "Skipping target and overlay pair " + target + " and "
-                        + pkg.getBaseCodePath()
+                        + pkg.getBaseApkPath()
                         + ": overlay ignored due to required system property: "
                         + propName + " with value: " + propValue;
                 Slog.i(TAG, message);
@@ -2679,7 +2674,7 @@
                                     "<meta-data> only supports string, integer, float, color, "
                                             + "boolean, and resource reference types: "
                                             + parser.getName() + " at "
-                                            + pkg.getBaseCodePath() + " "
+                                            + pkg.getBaseApkPath() + " "
                                             + parser.getPositionDescription());
                         } else {
                             return input.error("<meta-data> only supports string, integer, float, "
@@ -2716,7 +2711,7 @@
         try {
             ParseResult<SigningDetails> result = getSigningDetails(
                     input,
-                    pkg.getBaseCodePath(),
+                    pkg.getBaseApkPath(),
                     skipVerify,
                     pkg.isStaticSharedLibrary(),
                     signingDetails,
@@ -2862,7 +2857,7 @@
         boolean hasFeature(String feature);
 
         ParsingPackage startParsingPackage(@NonNull String packageName,
-                @NonNull String baseCodePath, @NonNull String codePath,
+                @NonNull String baseApkPath, @NonNull String path,
                 @NonNull TypedArray manifestArray, boolean isCoreApp);
     }
 }
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
index ba61de1..5da5fbf 100644
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -33,7 +33,6 @@
 /** @hide **/
 public class ParsingUtils {
 
-    // TODO(b/135203078): Consolidate log tags
     public static final String TAG = "PackageParsing";
 
     @Nullable
@@ -62,7 +61,7 @@
             return input.error("Bad element under " + parentTag + ": " + parser.getName());
         }
         Slog.w(TAG, "Unknown element under " + parentTag + ": "
-                + parser.getName() + " at " + pkg.getBaseCodePath() + " "
+                + parser.getName() + " at " + pkg.getBaseApkPath() + " "
                 + parser.getPositionDescription());
         XmlUtils.skipCurrentTag(parser);
         return input.success(null); // Type doesn't matter
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index c4caedc..cfefc016 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -24,15 +24,13 @@
 import android.content.pm.PackageUserState;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.text.TextUtils;
 
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -41,8 +39,6 @@
 /** @hide */
 public class ComponentParseUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
-
     public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
         return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
                 || intentInfo.hasAction(Intent.ACTION_SEND)
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 6927f3b..8c0bfef 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -24,7 +24,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
@@ -55,7 +54,7 @@
 /** @hide */
 public class ParsedActivityUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     @NonNull
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index 6811e06..dd2fb5b 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -36,8 +36,6 @@
 /** @hide */
 class ParsedComponentUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
-
     @NonNull
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     static <Component extends ParsedComponent> ParseResult<Component> parseComponent(
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 0ba92cc..6b797bc 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -22,7 +22,6 @@
 import android.os.Parcelable;
 import android.util.Pair;
 
-import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
 
 import java.util.ArrayList;
@@ -163,7 +162,7 @@
     }
 
     public String toString() {
-        return "ProviderIntentInfo{"
+        return "ParsedIntentInfo{"
                 + Integer.toHexString(System.identityHashCode(this))
                 + '}';
     }
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index 390f769..c0536bb 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -21,7 +21,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
@@ -43,7 +42,7 @@
 /** @hide */
 public class ParsedIntentInfoUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     @NonNull
     public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index f4c9914..f70d62b 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.content.IntentFilter;
 import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Configuration;
@@ -39,7 +39,7 @@
 /** @hide */
 class ParsedMainComponentUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     @NonNull
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -113,7 +113,7 @@
         ParsedIntentInfo intent = intentResult.getResult();
         int actionCount = intent.countActions();
         if (actionCount == 0 && failOnNoActions) {
-            Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseCodePath() + " "
+            Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseApkPath() + " "
                     + parser.getPositionDescription());
             // Backward-compat, do not actually fail
             return input.success(null);
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 1884a1e..894849a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -19,15 +19,15 @@
 import android.annotation.NonNull;
 import android.content.pm.PermissionInfo;
 import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.util.Slog;
 
 import com.android.internal.R;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -36,7 +36,7 @@
 /** @hide */
 public class ParsedPermissionUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     @NonNull
     public static ParseResult<ParsedPermission> parsePermission(ParsingPackage pkg, Resources res,
@@ -106,11 +106,6 @@
             sa.recycle();
         }
 
-        // TODO(b/135203078): This is impossible because of default value in above getInt
-        if (permission.protectionLevel == -1) {
-            return input.error("<permission> does not specify protectionLevel");
-        }
-
         permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
 
         if (permission.getProtectionFlags() != 0) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 8372707..9bff719 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -40,8 +40,6 @@
 /** @hide */
 public class ParsedProcessUtils {
 
-    private static final String TAG = ParsingUtils.TAG;
-
     @NonNull
     private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
             Resources res, XmlResourceParser parser, ParseInput input)
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index aa5ea8d..37cbeca 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -23,7 +23,6 @@
 import android.content.pm.PathPermission;
 import android.content.pm.ProviderInfo;
 import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
@@ -45,7 +44,7 @@
 /** @hide */
 public class ParsedProviderUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     @NonNull
     public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
@@ -247,7 +246,7 @@
                 }
 
                 Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at "
-                        + pkg.getBaseCodePath() + " " + parser.getPositionDescription());
+                        + pkg.getBaseApkPath() + " " + parser.getPositionDescription());
             }
 
             return input.success(provider);
@@ -293,7 +292,8 @@
                             "No readPermission or writePermission for <path-permission>");
                 }
                 Slog.w(TAG, "No readPermission or writePermission for <path-permission>: "
-                        + name + " at " + pkg.getBaseCodePath() + " " + parser.getPositionDescription());
+                        + name + " at " + pkg.getBaseApkPath() + " "
+                        + parser.getPositionDescription());
                 return input.success(provider);
             }
 
@@ -342,7 +342,7 @@
                 }
 
                 Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
-                        + name + " at " + pkg.getBaseCodePath()
+                        + name + " at " + pkg.getBaseApkPath()
                         + " "
                         + parser.getPositionDescription());
             }
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index a8d2d67..afe3c54 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -22,7 +22,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
@@ -43,8 +42,6 @@
 /** @hide */
 public class ParsedServiceUtils {
 
-    private static final String TAG = ParsingPackageUtils.TAG;
-
     @NonNull
     public static ParseResult<ParsedService> parseService(String[] separateProcesses,
             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 14992fb..0f90b53 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -95,7 +95,7 @@
                 return platformCompat.isChangeEnabled(changeId, appInfo);
             } catch (Exception e) {
                 // This shouldn't happen, but assume enforcement if it does
-                Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e);
+                Slog.wtf(TAG, "IPlatformCompat query failed", e);
                 return true;
             }
         });
@@ -125,7 +125,7 @@
     @Override
     public <ResultType> ParseResult<ResultType> success(ResultType result) {
         if (mErrorCode != PackageManager.INSTALL_SUCCEEDED) {
-            Slog.wtf(ParsingUtils.TAG, "Cannot set to success after set to error, was "
+            Slog.wtf(TAG, "Cannot set to success after set to error, was "
                     + mErrorMessage, mException);
         }
         mResult = result;
diff --git a/core/java/android/hardware/face/FaceSensorProperties.java b/core/java/android/hardware/face/FaceSensorProperties.java
index 1015724..23ad935 100644
--- a/core/java/android/hardware/face/FaceSensorProperties.java
+++ b/core/java/android/hardware/face/FaceSensorProperties.java
@@ -40,21 +40,27 @@
      * from above the HAL.
      */
     public final boolean supportsSelfIllumination;
+    /**
+     * Maximum number of enrollments a user/profile can have.
+     */
+    public final int maxTemplatesAllowed;
 
     /**
      * Initializes SensorProperties with specified values
      */
     public FaceSensorProperties(int sensorId, boolean supportsFaceDetection,
-            boolean supportsSelfIllumination) {
+            boolean supportsSelfIllumination, int maxTemplatesAllowed) {
         this.sensorId = sensorId;
         this.supportsFaceDetection = supportsFaceDetection;
         this.supportsSelfIllumination = supportsSelfIllumination;
+        this.maxTemplatesAllowed = maxTemplatesAllowed;
     }
 
     protected FaceSensorProperties(Parcel in) {
         sensorId = in.readInt();
         supportsFaceDetection = in.readBoolean();
         supportsSelfIllumination = in.readBoolean();
+        maxTemplatesAllowed = in.readInt();
     }
 
     public static final Creator<FaceSensorProperties> CREATOR =
@@ -80,5 +86,6 @@
         dest.writeInt(sensorId);
         dest.writeBoolean(supportsFaceDetection);
         dest.writeBoolean(supportsSelfIllumination);
+        dest.writeInt(maxTemplatesAllowed);
     }
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index b28551c..2fd0068 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -48,21 +48,25 @@
     // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
     // cannot be checked
     public final boolean resetLockoutRequiresHardwareAuthToken;
+    // Maximum number of enrollments a user/profile can have.
+    public final int maxTemplatesAllowed;
 
     /**
      * Initializes SensorProperties with specified values
      */
     public FingerprintSensorProperties(int sensorId, @SensorType int sensorType,
-            boolean resetLockoutRequiresHardwareAuthToken) {
+            boolean resetLockoutRequiresHardwareAuthToken, int maxTemplatesAllowed) {
         this.sensorId = sensorId;
         this.sensorType = sensorType;
         this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+        this.maxTemplatesAllowed = maxTemplatesAllowed;
     }
 
     protected FingerprintSensorProperties(Parcel in) {
         sensorId = in.readInt();
         sensorType = in.readInt();
         resetLockoutRequiresHardwareAuthToken = in.readBoolean();
+        maxTemplatesAllowed = in.readInt();
     }
 
     public static final Creator<FingerprintSensorProperties> CREATOR =
@@ -88,5 +92,6 @@
         dest.writeInt(sensorId);
         dest.writeInt(sensorType);
         dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
+        dest.writeInt(maxTemplatesAllowed);
     }
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 8e865e7..1db544c 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1027,6 +1027,18 @@
 
         /**
          * R.
+         *
+         * <p>Applications targeting this or a later release will get these new changes in behavior.
+         * For more information about this release, see the
+         * <a href="/about/versions/11">Android 11 overview</a>.</p>
+         * <ul>
+         * <li><a href="/about/versions/11/behavior-changes-all">Behavior changes: all apps</a></li>
+         * <li><a href="/about/versions/11/behavior-changes-11">Behavior changes: Apps targeting
+         * Android 11</a></li>
+         * <li><a href="/about/versions/11/non-sdk-11">Updates to non-SDK interface restrictions
+         * in Android 11</a></li>
+         * </ul>
+         *
          */
         public static final int R = 30;
 
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index e05991b..a79a1cf 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -118,7 +118,7 @@
      * affects them.
      */
     public abstract void onAppOpsChanged(int code, int uid,
-            @Nullable String packageName, int mode);
+            @Nullable String packageName, int mode, int previousMode);
 
     /**
      * Asks the StorageManager to reset all state for the provided user; this will result
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 739e169..18337b6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6801,6 +6801,13 @@
         public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri";
 
         /**
+         * Whether to draw text in bold.
+         *
+         * @hide
+         */
+        public static final String FORCE_BOLD_TEXT = "force_bold_text";
+
+        /**
          * Whether to speak passwords while in accessibility mode.
          *
          * @deprecated The speaking of passwords is controlled by individual accessibility services.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e083417..19860eb 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -71,6 +71,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.HandlerCaller;
@@ -186,17 +187,11 @@
         int mCurWindowFlags = mWindowFlags;
         int mCurWindowPrivateFlags = mWindowPrivateFlags;
         Rect mPreviewSurfacePosition;
-        final Rect mVisibleInsets = new Rect();
-        final Rect mWinFrame = new Rect();
-        final Rect mContentInsets = new Rect();
-        final Rect mStableInsets = new Rect();
+        final ClientWindowFrames mWinFrames = new ClientWindowFrames();
         final Rect mDispatchedContentInsets = new Rect();
         final Rect mDispatchedStableInsets = new Rect();
         final Rect mFinalSystemInsets = new Rect();
         final Rect mFinalStableInsets = new Rect();
-        final Rect mBackdropFrame = new Rect();
-        final DisplayCutout.ParcelableWrapper mDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
         DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
         final InsetsState mInsetsState = new InsetsState();
         final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
@@ -332,11 +327,9 @@
 
         final BaseIWindow mWindow = new BaseIWindow() {
             @Override
-            public void resized(Rect frame, Rect contentInsets,
-                    Rect visibleInsets, Rect stableInsets, boolean reportDraw,
-                    MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
-                    boolean alwaysConsumeSystemBars, int displayId,
-                    DisplayCutout.ParcelableWrapper displayCutout) {
+            public void resized(ClientWindowFrames frames, boolean reportDraw,
+                    MergedConfiguration mergedConfiguration, boolean forceLayout,
+                    boolean alwaysConsumeSystemBars, int displayId) {
                 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0);
                 mCaller.sendMessage(msg);
@@ -749,10 +742,7 @@
                     out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
             out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
                     out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
-            out.print(prefix); out.print("mVisibleInsets=");
-                    out.print(mVisibleInsets.toShortString());
-                    out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
-                    out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
+            out.print(prefix); out.println("mWinFrames="); out.println(mWinFrames);
             out.print(prefix); out.print("mConfiguration=");
                     out.println(mMergedConfiguration.getMergedConfiguration());
             out.print(prefix); out.print("mLayout="); out.println(mLayout);
@@ -890,8 +880,8 @@
                         InputChannel inputChannel = new InputChannel();
 
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
-                                mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
-                                mDisplayCutout, inputChannel,
+                                mDisplay.getDisplayId(), mWinFrames.frame, mWinFrames.contentInsets,
+                                mWinFrames.stableInsets, mWinFrames.displayCutout, inputChannel,
                                 mInsetsState, mTempControls) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
@@ -914,34 +904,32 @@
 
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
-                            View.VISIBLE, 0, -1, mWinFrame, mContentInsets,
-                            mVisibleInsets, mStableInsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceControl,
+                            View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
                             mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
                     if (mSurfaceControl.isValid()) {
                         mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
                     }
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
-                            + ", frame=" + mWinFrame);
+                            + ", frame=" + mWinFrames);
 
-                    int w = mWinFrame.width();
-                    int h = mWinFrame.height();
+                    int w = mWinFrames.frame.width();
+                    int h = mWinFrames.frame.height();
 
                     if (!fixedSize) {
                         final Rect padding = mIWallpaperEngine.mDisplayPadding;
                         w += padding.left + padding.right;
                         h += padding.top + padding.bottom;
-                        mContentInsets.left += padding.left;
-                        mContentInsets.top += padding.top;
-                        mContentInsets.right += padding.right;
-                        mContentInsets.bottom += padding.bottom;
-                        mStableInsets.left += padding.left;
-                        mStableInsets.top += padding.top;
-                        mStableInsets.right += padding.right;
-                        mStableInsets.bottom += padding.bottom;
-                        mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top,
-                                -padding.right, -padding.bottom));
+                        mWinFrames.contentInsets.left += padding.left;
+                        mWinFrames.contentInsets.top += padding.top;
+                        mWinFrames.contentInsets.right += padding.right;
+                        mWinFrames.contentInsets.bottom += padding.bottom;
+                        mWinFrames.stableInsets.left += padding.left;
+                        mWinFrames.stableInsets.top += padding.top;
+                        mWinFrames.stableInsets.right += padding.right;
+                        mWinFrames.stableInsets.bottom += padding.bottom;
+                        mWinFrames.displayCutout.set(mWinFrames.displayCutout.get().inset(
+                                -padding.left, -padding.top, -padding.right, -padding.bottom));
                     } else {
                         w = myWidth;
                         h = myHeight;
@@ -960,9 +948,10 @@
                         Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight);
                     }
 
-                    insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
-                    insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
-                    insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get());
+                    final DisplayCutout displayCutout = mWinFrames.displayCutout.get();
+                    insetsChanged |= !mDispatchedContentInsets.equals(mWinFrames.contentInsets);
+                    insetsChanged |= !mDispatchedStableInsets.equals(mWinFrames.stableInsets);
+                    insetsChanged |= !mDispatchedDisplayCutout.equals(displayCutout);
 
                     mSurfaceHolder.setSurfaceFrameSize(w, h);
                     mSurfaceHolder.mSurfaceLock.unlock();
@@ -1021,9 +1010,9 @@
                         }
 
                         if (insetsChanged) {
-                            mDispatchedContentInsets.set(mContentInsets);
-                            mDispatchedStableInsets.set(mStableInsets);
-                            mDispatchedDisplayCutout = mDisplayCutout.get();
+                            mDispatchedContentInsets.set(mWinFrames.contentInsets);
+                            mDispatchedStableInsets.set(mWinFrames.stableInsets);
+                            mDispatchedDisplayCutout = displayCutout;
                             mFinalStableInsets.set(mDispatchedStableInsets);
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
                                     mFinalStableInsets,
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 4b42209..5fd192a 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -183,10 +183,9 @@
             @NonNull String packageName, @Nullable String featureId) {
         if (DBG) Log.d(TAG, "checkPermissions");
         if (forDataDelivery) {
-            if (PermissionChecker.checkCallingPermissionForDataDelivery(this,
+            if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
                     android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
-                    null /*message*/)
-                             == PermissionChecker.PERMISSION_GRANTED) {
+                    null /*message*/) == PermissionChecker.PERMISSION_GRANTED) {
                 return true;
             }
         } else {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index e338fd97..0973608 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,6 +65,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
+        DEFAULT_FLAGS.put("settings_silky_home", "false");
     }
 
     /**
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index e09bf9d..94e641c 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -29,6 +29,7 @@
 import android.view.IScrollCaptureController;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -52,11 +53,9 @@
      */
     void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
 
-    void resized(in Rect frame, in Rect contentInsets,
-            in Rect visibleInsets, in Rect stableInsets, boolean reportDraw,
-            in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
-            boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
-            in DisplayCutout.ParcelableWrapper displayCutout);
+    void resized(in ClientWindowFrames frames, boolean reportDraw,
+            in MergedConfiguration newMergedConfiguration,
+            boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId);
 
     /**
      * Called when the window location in parent display has changed. The offset will only be a
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 00fc672..8e875d7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -393,11 +393,6 @@
     oneway void setRecentsVisibility(boolean visible);
 
     /**
-     * Called by System UI to notify of changes to the visibility of PIP.
-     */
-    oneway void setPipVisibility(boolean visible);
-
-    /**
      * Called by System UI to enable or disable haptic feedback on the navigation bar buttons.
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 819e89b..70850d8 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -34,6 +34,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
+import android.window.ClientWindowFrames;
 
 import java.util.List;
 
@@ -107,10 +108,7 @@
      */
     int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility,
-            int flags, long frameNumber, out Rect outFrame,
-            out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
-            out Rect outBackdropFrame,
-            out DisplayCutout.ParcelableWrapper displayCutout,
+            int flags, long frameNumber, out ClientWindowFrames outFrames,
             out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
             out InsetsState insetsState, out InsetsSourceControl[] activeControls,
             out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);
@@ -152,12 +150,6 @@
             in Rect visibleInsets, in Region touchableRegion);
 
     /**
-     * Return the current display size in which the window is being laid out,
-     * accounting for screen decorations around it.
-     */
-    void getDisplayFrame(IWindow window, out Rect outDisplayFrame);
-
-    /**
      * Called when the client has finished drawing the surface, if needed.
      *
      * @param postDrawTransaction transaction filled by the client that can be
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index e249c77..0ab1751 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1908,8 +1908,6 @@
     /**
      * Returns whether this key will be sent to the
      * {@link android.media.session.MediaSession.Callback} if not handled.
-     *
-     * @hide
      */
     public static final boolean isMediaSessionKey(int keyCode) {
         switch (keyCode) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3af8958..2bd3c06 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,9 +65,6 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
@@ -90,10 +87,10 @@
     private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDisconnect(long nativeObject);
-    private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
-            ScreenCaptureListener captureListener);
-    private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
-            ScreenCaptureListener captureListener);
+    private static native ScreenshotHardwareBuffer nativeCaptureDisplay(
+            DisplayCaptureArgs captureArgs);
+    private static native ScreenshotHardwareBuffer nativeCaptureLayers(
+            LayerCaptureArgs captureArgs);
     private static native long nativeMirrorSurface(long mirrorOfObject);
     private static native long nativeCreateTransaction();
     private static native long nativeGetNativeTransactionFinalizer();
@@ -224,6 +221,8 @@
     private static native void nativeReleaseFrameRateFlexibilityToken(long token);
     private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
             int transformHint);
+    private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
+                                                      IBinder focusedToken, int displayId);
 
     @Nullable
     @GuardedBy("mLock")
@@ -496,8 +495,6 @@
     private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
     private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
 
-    private static final int SCREENSHOT_WAIT_TIME_S = 1;
-
     private void assignNativeObject(long nativeObject, String callsite) {
         if (mNativeObject != 0) {
             release();
@@ -616,13 +613,6 @@
     }
 
     /**
-     * @hide
-     */
-    public abstract static class ScreenCaptureListener {
-        abstract void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
-    }
-
-    /**
      * A common arguments class used for various screenshot requests. This contains arguments that
      * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
      * @hide
@@ -697,7 +687,7 @@
     /**
      * The arguments class used to make display capture requests.
      *
-     * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
+     * @see #nativeCaptureDisplay(DisplayCaptureArgs)
      * @hide
      */
     public static class DisplayCaptureArgs extends CaptureArgs {
@@ -2238,46 +2228,13 @@
     }
 
     /**
-     * @param captureArgs Arguments about how to take the screenshot
-     * @param captureListener A listener to receive the screenshot callback
-     * @hide
-     */
-    public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
-            @NonNull ScreenCaptureListener captureListener) {
-        return nativeCaptureDisplay(captureArgs, captureListener);
-    }
-
-    /**
      * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
      * the content.
      *
      * @hide
      */
     public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
-        final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer =
-                new AtomicReference<>(null);
-
-        final CountDownLatch countDownLatch = new CountDownLatch(1);
-        ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() {
-            @Override
-            void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
-                outHardwareBuffer.set(hardwareBuffer);
-                countDownLatch.countDown();
-            }
-        };
-
-        int status = captureDisplay(captureArgs, screenCaptureListener);
-        if (status != 0) {
-            return null;
-        }
-
-        try {
-            countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to wait for captureDisplay result", e);
-        }
-
-        return outHardwareBuffer.get();
+        return nativeCaptureDisplay(captureArgs);
     }
 
     /**
@@ -2322,37 +2279,14 @@
                 .setPixelFormat(format)
                 .build();
 
-        return captureLayers(captureArgs);
+        return nativeCaptureLayers(captureArgs);
     }
 
     /**
      * @hide
      */
     public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
-        final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer =
-                new AtomicReference<>(null);
-
-        final CountDownLatch countDownLatch = new CountDownLatch(1);
-        ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() {
-            @Override
-            void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
-                outHardwareBuffer.set(hardwareBuffer);
-                countDownLatch.countDown();
-            }
-        };
-
-        int status = captureLayers(captureArgs, screenCaptureListener);
-        if (status != 0) {
-            return null;
-        }
-
-        try {
-            countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to wait for captureLayers result", e);
-        }
-
-        return outHardwareBuffer.get();
+        return nativeCaptureLayers(captureArgs);
     }
 
     /**
@@ -2369,17 +2303,7 @@
                 .setExcludeLayers(exclude)
                 .build();
 
-        return captureLayers(captureArgs);
-    }
-
-    /**
-     * @param captureArgs Arguments about how to take the screenshot
-     * @param captureListener A listener to receive the screenshot callback
-     * @hide
-     */
-    public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
-            @NonNull ScreenCaptureListener captureListener) {
-        return nativeCaptureLayers(captureArgs, captureListener);
+        return nativeCaptureLayers(captureArgs);
     }
 
     /**
@@ -3262,6 +3186,39 @@
         }
 
         /**
+         * Sets focus on the window identified by the input {@code token} if the window is focusable
+         * otherwise the request is dropped.
+         *
+         * If the window is not visible, the request will be queued until the window becomes
+         * visible or the request is overrriden by another request. The currently focused window
+         * will lose focus immediately. This is to send the newly focused window any focus
+         * dispatched events that occur while it is completing its first draw.
+         *
+         * @hide
+         */
+        public Transaction setFocusedWindow(@NonNull IBinder token, int displayId) {
+            nativeSetFocusedWindow(mNativeObject, token,  null /* focusedToken */, displayId);
+            return this;
+        }
+
+        /**
+         * Set focus on the window identified by the input {@code token} if the window identified by
+         * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not
+         * have focus, the request is dropped.
+         *
+         * This is used by forward focus transfer requests from clients that host embedded windows,
+         * and want to transfer focus to/from them.
+         *
+         * @hide
+         */
+        public Transaction requestFocusTransfer(@NonNull IBinder token,
+                                                @NonNull IBinder focusedToken,
+                                                int displayId) {
+            nativeSetFocusedWindow(mNativeObject, token, focusedToken, displayId);
+            return this;
+        }
+
+         /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
          *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8917821..92a0f63 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14940,20 +14940,12 @@
      * inside.  In effect, this tells you the available area where content can
      * be placed and remain visible to users.
      *
-     * <p>This function requires an IPC back to the window manager to retrieve
-     * the requested information, so should not be used in performance critical
-     * code like drawing.
-     *
      * @param outRect Filled in with the visible display frame.  If the view
      * is not attached to a window, this is simply the raw display size.
      */
     public void getWindowVisibleDisplayFrame(Rect outRect) {
         if (mAttachInfo != null) {
-            try {
-                mAttachInfo.mSession.getDisplayFrame(mAttachInfo.mWindow, outRect);
-            } catch (RemoteException e) {
-                return;
-            }
+            mAttachInfo.mViewRootImpl.getDisplayFrame(outRect);
             // XXX This is really broken, and probably all needs to be done
             // in the window manager, and we need to know more about whether
             // we want the area behind or in front of the IME.
@@ -14979,11 +14971,7 @@
     @UnsupportedAppUsage
     public void getWindowDisplayFrame(Rect outRect) {
         if (mAttachInfo != null) {
-            try {
-                mAttachInfo.mSession.getDisplayFrame(mAttachInfo.mWindow, outRect);
-            } catch (RemoteException e) {
-                return;
-            }
+            mAttachInfo.mViewRootImpl.getDisplayFrame(outRect);
             return;
         }
         // The view is not attached to a display so we don't have a context.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a5be75..4303d70 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -33,7 +33,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -755,11 +754,7 @@
 
         try {
             Rect outRect = new Rect();
-            try {
-                root.mAttachInfo.mSession.getDisplayFrame(root.mAttachInfo.mWindow, outRect);
-            } catch (RemoteException e) {
-                // Ignore
-            }
+            root.mAttachInfo.mViewRootImpl.getDisplayFrame(outRect);
 
             clientStream.writeInt(outRect.width());
             clientStream.writeInt(outRect.height());
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f3a8d97..af839d4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -137,7 +137,6 @@
 import android.view.Window.OnContentApplyWindowInsetsListener;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -160,6 +159,7 @@
 import android.view.contentcapture.MainContentCaptureSession;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -465,6 +465,7 @@
     // used in relayout to get SurfaceControl size
     // for BLAST adapter surface setup
     private final Point mSurfaceSize = new Point();
+    private final Point mLastSurfaceSize = new Point();
 
     final Rect mTempRect; // used in the transaction to not thrash the heap.
     final Rect mVisRect; // used to retrieve visible rect of focused view.
@@ -559,8 +560,11 @@
     boolean mAdded;
     boolean mAddedTouchMode;
 
-    final Rect mTmpFrame = new Rect();
-    final Rect mTmpRect = new Rect();
+    /**
+     * It usually keeps the latest layout result from {@link IWindow#resized} or
+     * {@link IWindowSession#relayout}.
+     */
+    private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
 
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
@@ -1032,11 +1036,11 @@
                     collectViewAttributes();
                     adjustLayoutParamsForCompatibility(mWindowAttributes);
                     res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
+                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrames.frame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                             mAttachInfo.mDisplayCutout, inputChannel,
                             mTempInsets, mTempControls);
-                    setFrame(mTmpFrame);
+                    setFrame(mTmpFrames.frame);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -1474,6 +1478,55 @@
         scheduleTraversals();
     }
 
+    /** Handles messages {@link #MSG_RESIZED} and {@link #MSG_RESIZED_REPORT}. */
+    private void handleResized(int msg, SomeArgs args) {
+        if (!mAdded) {
+            return;
+        }
+
+        final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
+        final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
+        final boolean forceNextWindowRelayout = args.argi1 != 0;
+        final int displayId = args.argi3;
+        final Rect backdropFrame = frames.backdropFrame;
+        final DisplayCutout displayCutout = frames.displayCutout.get();
+
+        final boolean frameChanged = !mWinFrame.equals(frames.frame);
+        final boolean cutoutChanged = !mPendingDisplayCutout.get().equals(displayCutout);
+        final boolean backdropFrameChanged = !mPendingBackDropFrame.equals(backdropFrame);
+        final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
+        final boolean displayChanged = mDisplay.getDisplayId() != displayId;
+        if (msg == MSG_RESIZED && !frameChanged && !cutoutChanged && !backdropFrameChanged
+                && !configChanged && !displayChanged && !forceNextWindowRelayout) {
+            return;
+        }
+
+        if (configChanged) {
+            // If configuration changed - notify about that and, maybe, about move to display.
+            performConfigurationChange(mergedConfiguration, false /* force */,
+                    displayChanged ? displayId : INVALID_DISPLAY /* same display */);
+        } else if (displayChanged) {
+            // Moved to display without config change - report last applied one.
+            onMovedToDisplay(displayId, mLastConfigurationFromResources);
+        }
+
+        setFrame(frames.frame);
+        mTmpFrames.displayFrame.set(frames.displayFrame);
+        mPendingDisplayCutout.set(displayCutout);
+        mPendingBackDropFrame.set(backdropFrame);
+        mForceNextWindowRelayout = forceNextWindowRelayout;
+        mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
+
+        if (msg == MSG_RESIZED_REPORT) {
+            reportNextDraw();
+        }
+
+        if (mView != null && (frameChanged || cutoutChanged || configChanged)) {
+            forceLayout(mView);
+        }
+        requestLayout();
+    }
+
     private final DisplayListener mDisplayListener = new DisplayListener() {
         @Override
         public void onDisplayChanged(int displayId) {
@@ -2641,9 +2694,12 @@
                 }
 
                 cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout);
-                surfaceSizeChanged = (relayoutResult
-                        & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
-                final boolean alwaysConsumeSystemBarsChanged =
+                surfaceSizeChanged = false;
+                if (!mLastSurfaceSize.equals(mSurfaceSize)) {
+                    surfaceSizeChanged = true;
+                    mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
+                }
+                  final boolean alwaysConsumeSystemBarsChanged =
                         mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
                 updateColorModeIfNeeded(lp.getColorMode());
                 surfaceCreated = !hadSurface && mSurface.isValid();
@@ -4919,60 +4975,13 @@
                 case MSG_DISPATCH_GET_NEW_SURFACE:
                     handleGetNewSurface();
                     break;
-                case MSG_RESIZED: {
-                    // Recycled in the fall through...
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    if (mWinFrame.equals(args.arg1)
-                            && mPendingDisplayCutout.get().equals(args.arg9)
-                            && mPendingBackDropFrame.equals(args.arg8)
-                            && mLastReportedMergedConfiguration.equals(args.arg4)
-                            && args.argi1 == 0
-                            && mDisplay.getDisplayId() == args.argi3) {
-                        break;
-                    }
-                } // fall through...
-                case MSG_RESIZED_REPORT:
-                    if (mAdded) {
-                        SomeArgs args = (SomeArgs) msg.obj;
-
-                        final int displayId = args.argi3;
-                        MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
-                        final boolean displayChanged = mDisplay.getDisplayId() != displayId;
-                        boolean configChanged = false;
-
-                        if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
-                            // If configuration changed - notify about that and, maybe,
-                            // about move to display.
-                            performConfigurationChange(mergedConfiguration, false /* force */,
-                                    displayChanged
-                                            ? displayId : INVALID_DISPLAY /* same display */);
-                            configChanged = true;
-                        } else if (displayChanged) {
-                            // Moved to display without config change - report last applied one.
-                            onMovedToDisplay(displayId, mLastConfigurationFromResources);
-                        }
-
-                        final boolean framesChanged = !mWinFrame.equals(args.arg1)
-                                || !mPendingDisplayCutout.get().equals(args.arg9);
-
-                        setFrame((Rect) args.arg1);
-                        mPendingDisplayCutout.set((DisplayCutout) args.arg9);
-                        mPendingBackDropFrame.set((Rect) args.arg8);
-                        mForceNextWindowRelayout = args.argi1 != 0;
-                        mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
-
-                        args.recycle();
-
-                        if (msg.what == MSG_RESIZED_REPORT) {
-                            reportNextDraw();
-                        }
-
-                        if (mView != null && (framesChanged || configChanged)) {
-                            forceLayout(mView);
-                        }
-                        requestLayout();
-                    }
+                case MSG_RESIZED:
+                case MSG_RESIZED_REPORT: {
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    handleResized(msg.what, args);
+                    args.recycle();
                     break;
+                }
                 case MSG_INSETS_CHANGED:
                     mInsetsController.onStateChanged((InsetsState) msg.obj);
                     break;
@@ -5007,11 +5016,11 @@
                         final int h = mWinFrame.height();
                         final int l = msg.arg1;
                         final int t = msg.arg2;
-                        mTmpFrame.left = l;
-                        mTmpFrame.right = l + w;
-                        mTmpFrame.top = t;
-                        mTmpFrame.bottom = t + h;
-                        setFrame(mTmpFrame);
+                        mTmpFrames.frame.left = l;
+                        mTmpFrames.frame.right = l + w;
+                        mTmpFrames.frame.top = t;
+                        mTmpFrames.frame.bottom = t + h;
+                        setFrame(mTmpFrames.frame);
 
                         mPendingBackDropFrame.set(mWinFrame);
                         maybeHandleWindowMove(mWinFrame);
@@ -7418,9 +7427,10 @@
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
-                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
-                mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
+                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                 mTempControls, mSurfaceSize, mBlastSurfaceControl);
+        mPendingDisplayCutout.set(mTmpFrames.displayCutout);
+        mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
         if (mSurfaceControl.isValid()) {
             if (!useBLAST()) {
                 mSurface.copyFrom(mSurfaceControl);
@@ -7446,9 +7456,9 @@
         }
 
         if (mTranslator != null) {
-            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
+            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrames.frame);
         }
-        setFrame(mTmpFrame);
+        setFrame(mTmpFrames.frame);
         mInsetsController.onStateChanged(mTempInsets);
         mInsetsController.onControlsChanged(mTempControls);
         return relayoutResult;
@@ -7460,6 +7470,14 @@
     }
 
     /**
+     * Gets the current display size in which the window is being laid out, accounting for screen
+     * decorations around it.
+     */
+    void getDisplayFrame(Rect outFrame) {
+        outFrame.set(mTmpFrames.displayFrame);
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -7742,11 +7760,14 @@
     }
 
     @UnsupportedAppUsage
-    private void dispatchResized(Rect frame, Rect contentInsets,
-            Rect visibleInsets, Rect stableInsets, boolean reportDraw,
-            MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeSystemBars, int displayId,
-            DisplayCutout.ParcelableWrapper displayCutout) {
+    private void dispatchResized(ClientWindowFrames frames, boolean reportDraw,
+            MergedConfiguration mergedConfiguration, boolean forceLayout,
+            boolean alwaysConsumeSystemBars, int displayId) {
+        final Rect frame = frames.frame;
+        final Rect contentInsets = frames.contentInsets;
+        final Rect visibleInsets = frames.visibleInsets;
+        final Rect stableInsets = frames.stableInsets;
+        final Rect backDropFrame = frames.backdropFrame;
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -7773,14 +7794,9 @@
         }
         SomeArgs args = SomeArgs.obtain();
         final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
-        args.arg1 = sameProcessCall ? new Rect(frame) : frame;
-        args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets;
-        args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
-        args.arg4 = sameProcessCall && mergedConfiguration != null
+        args.arg1 = sameProcessCall ? new ClientWindowFrames(frames) : frames;
+        args.arg2 = sameProcessCall && mergedConfiguration != null
                 ? new MergedConfiguration(mergedConfiguration) : mergedConfiguration;
-        args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
-        args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
-        args.arg9 = displayCutout.get(); // DisplayCutout is immutable.
         args.argi1 = forceLayout ? 1 : 0;
         args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
         args.argi3 = displayId;
@@ -9074,17 +9090,13 @@
         }
 
         @Override
-        public void resized(Rect frame, Rect contentInsets,
-                Rect visibleInsets, Rect stableInsets, boolean reportDraw,
-                MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeSystemBars, int displayId,
-                DisplayCutout.ParcelableWrapper displayCutout) {
+        public void resized(ClientWindowFrames frames, boolean reportDraw,
+                MergedConfiguration mergedConfiguration, boolean forceLayout,
+                boolean alwaysConsumeSystemBars, int displayId) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
-                viewAncestor.dispatchResized(frame, contentInsets,
-                        visibleInsets, stableInsets, reportDraw, mergedConfiguration,
-                        backDropFrame, forceLayout, alwaysConsumeSystemBars, displayId,
-                        displayCutout);
+                viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, forceLayout,
+                        alwaysConsumeSystemBars, displayId);
             }
         }
 
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 060311e..368918d 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -26,6 +26,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.MergedConfiguration;
+import android.window.ClientWindowFrames;
 
 import java.util.HashMap;
 import java.util.Objects;
@@ -224,9 +225,7 @@
     @Override
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams inAttrs,
             int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
-            Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
-            Rect outStableInsets, Rect outBackdropFrame,
-            DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
+            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
             SurfaceControl outBLASTSurfaceControl) {
@@ -255,7 +254,8 @@
             t.hide(sc).apply();
             outSurfaceControl.release();
         }
-        outFrame.set(0, 0, attrs.width, attrs.height);
+        outFrames.frame.set(0, 0, attrs.width, attrs.height);
+        outFrames.displayFrame.set(outFrames.frame);
 
         mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
 
@@ -292,11 +292,6 @@
     }
 
     @Override
-    public void getDisplayFrame(android.view.IWindow window,
-            android.graphics.Rect outDisplayFrame) {
-    }
-
-    @Override
     public void finishDrawing(android.view.IWindow window,
             android.view.SurfaceControl.Transaction postDrawTransaction) {
         synchronized (this) {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java b/core/java/android/window/ClientWindowFrames.aidl
similarity index 61%
copy from packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
copy to core/java/android/window/ClientWindowFrames.aidl
index e65f19d..22bbea9 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
+++ b/core/java/android/window/ClientWindowFrames.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,17 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.keyguard.dagger;
+package android.window;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface ContainerView {
-}
+parcelable ClientWindowFrames;
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
new file mode 100644
index 0000000..0523e64
--- /dev/null
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -0,0 +1,125 @@
+/*
+ * 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 android.window;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.DisplayCutout;
+
+/**
+ * The window frame container class used by client side for layout.
+ * @hide
+ */
+public class ClientWindowFrames implements Parcelable {
+    /** The actual window bounds. */
+    public final @NonNull Rect frame;
+
+    /**
+     * The container frame that is usually the same as display size. It may exclude the area of
+     * insets if the window layout parameter has specified fit-insets-sides.
+     */
+    public final @NonNull Rect displayFrame;
+
+    /** The background area while the window is resizing. */
+    public final @NonNull Rect backdropFrame;
+
+    /** The area cut from the display. */
+    public final @NonNull DisplayCutout.ParcelableWrapper displayCutout;
+
+    // TODO(b/149813814): Remove legacy insets.
+    public final Rect contentInsets;
+    public final Rect visibleInsets;
+    public final Rect stableInsets;
+
+    public ClientWindowFrames() {
+        frame = new Rect();
+        displayFrame = new Rect();
+        backdropFrame = new Rect();
+        displayCutout = new DisplayCutout.ParcelableWrapper();
+        contentInsets = new Rect();
+        visibleInsets = new Rect();
+        stableInsets = new Rect();
+    }
+
+    public ClientWindowFrames(ClientWindowFrames other) {
+        frame = new Rect(other.frame);
+        displayFrame = new Rect(other.displayFrame);
+        backdropFrame = new Rect(other.backdropFrame);
+        displayCutout = new DisplayCutout.ParcelableWrapper(other.displayCutout.get());
+        contentInsets = new Rect(other.contentInsets);
+        visibleInsets = new Rect(other.visibleInsets);
+        stableInsets = new Rect(other.stableInsets);
+    }
+
+    private ClientWindowFrames(Parcel in) {
+        frame = Rect.CREATOR.createFromParcel(in);
+        displayFrame = Rect.CREATOR.createFromParcel(in);
+        backdropFrame = Rect.CREATOR.createFromParcel(in);
+        displayCutout = DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in);
+        contentInsets = Rect.CREATOR.createFromParcel(in);
+        visibleInsets = Rect.CREATOR.createFromParcel(in);
+        stableInsets = Rect.CREATOR.createFromParcel(in);
+    }
+
+    /** Needed for AIDL out parameters. */
+    public void readFromParcel(Parcel in) {
+        frame.set(Rect.CREATOR.createFromParcel(in));
+        displayFrame.set(Rect.CREATOR.createFromParcel(in));
+        backdropFrame.set(Rect.CREATOR.createFromParcel(in));
+        displayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
+        contentInsets.set(Rect.CREATOR.createFromParcel(in));
+        visibleInsets.set(Rect.CREATOR.createFromParcel(in));
+        stableInsets.set(Rect.CREATOR.createFromParcel(in));
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        frame.writeToParcel(dest, flags);
+        displayFrame.writeToParcel(dest, flags);
+        backdropFrame.writeToParcel(dest, flags);
+        displayCutout.writeToParcel(dest, flags);
+        contentInsets.writeToParcel(dest, flags);
+        visibleInsets.writeToParcel(dest, flags);
+        stableInsets.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(32);
+        return "ClientWindowFrames{frame=" + frame.toShortString(sb)
+                + " display=" + displayFrame.toShortString(sb)
+                + " backdrop=" + backdropFrame.toShortString(sb)
+                + " cutout=" + displayCutout + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ClientWindowFrames> CREATOR = new Creator<ClientWindowFrames>() {
+        public ClientWindowFrames createFromParcel(Parcel in) {
+            return new ClientWindowFrames(in);
+        }
+
+        public ClientWindowFrames[] newArray(int size) {
+            return new ClientWindowFrames[size];
+        }
+    };
+}
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index 9013da3..db27d62 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.ActivityView;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
@@ -357,40 +358,40 @@
     private class TaskStackListenerImpl extends TaskStackListener {
 
         @Override
-        public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo)
+        public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo runningTaskInfo)
                 throws RemoteException {
             if (!isInitialized()) {
                 return;
             }
-            if (taskInfo.displayId != getDisplayId()) {
+            if (runningTaskInfo.displayId != getDisplayId()) {
                 return;
             }
-            ActivityManager.StackInfo stackInfo = getTopMostStackInfo();
-            if (stackInfo == null) {
+            RootTaskInfo taskInfo = getTopMostRootTaskInfo();
+            if (taskInfo == null) {
                 return;
             }
             // Found the topmost stack on target display. Now check if the topmost task's
             // description changed.
-            if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+            if (runningTaskInfo.taskId == taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]) {
                 mHost.post(()-> mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
-                        taskInfo.taskDescription.getBackgroundColor()));
+                        runningTaskInfo.taskDescription.getBackgroundColor()));
             }
         }
 
         @Override
-        public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
+        public void onTaskMovedToFront(ActivityManager.RunningTaskInfo runningTaskInfo)
                 throws RemoteException {
             if (!isInitialized() || mListener == null
-                    || taskInfo.displayId != getDisplayId()) {
+                    || runningTaskInfo.displayId != getDisplayId()) {
                 return;
             }
 
-            ActivityManager.StackInfo stackInfo = getTopMostStackInfo();
-            // if StackInfo was null or unrelated to the "move to front" then there's no use
+            RootTaskInfo taskInfo = getTopMostRootTaskInfo();
+            // if TaskInfo was null or unrelated to the "move to front" then there's no use
             // notifying the callback
-            if (stackInfo != null
-                    && taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
-                mListener.onTaskMovedToFront(taskInfo.taskId);
+            if (taskInfo != null && runningTaskInfo.taskId == taskInfo.childTaskIds[
+                    taskInfo.childTaskIds.length - 1]) {
+                mListener.onTaskMovedToFront(runningTaskInfo.taskId);
             }
         }
 
@@ -400,11 +401,11 @@
                 return;
             }
 
-            ActivityManager.StackInfo stackInfo = getTopMostStackInfo();
-            // if StackInfo was null or unrelated to the task creation then there's no use
+            RootTaskInfo taskInfo = getTopMostRootTaskInfo();
+            // if TaskInfo was null or unrelated to the task creation then there's no use
             // notifying the callback
-            if (stackInfo != null
-                    && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
+            if (taskInfo != null
+                    && taskId == taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]) {
                 mListener.onTaskCreated(taskId, componentName);
             }
         }
@@ -420,16 +421,16 @@
             mListener.onTaskRemovalStarted(taskInfo.taskId);
         }
 
-        private ActivityManager.StackInfo getTopMostStackInfo() throws RemoteException {
+        private RootTaskInfo getTopMostRootTaskInfo() throws RemoteException {
             // Find the topmost task on our virtual display - it will define the background
             // color of the surface view during resizing.
             final int displayId = getDisplayId();
-            final List<ActivityManager.StackInfo> stackInfoList =
-                    mActivityTaskManager.getAllStackInfosOnDisplay(displayId);
-            if (stackInfoList.isEmpty()) {
+            final List<RootTaskInfo> taskInfoList =
+                    mActivityTaskManager.getAllRootTaskInfosOnDisplay(displayId);
+            if (taskInfoList.isEmpty()) {
                 return null;
             }
-            return stackInfoList.get(0);
+            return taskInfoList.get(0);
         }
     }
 }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index da26930..8b4fddb 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -390,9 +390,9 @@
     public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";
 
     /**
-     * (boolean) Whether to enable user-drag resizing for PIP.
+     * (boolean) Whether to enable pinch resizing for PIP.
      */
-    public static final String PIP_USER_RESIZE = "pip_user_resize";
+    public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";
 
     /**
      * (float) Bottom height in DP for Back Gesture.
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 3b5cf48..b38f623e 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -296,7 +296,7 @@
             if (p.getOverlayTarget() != null && isSystem) {
                 overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
                         p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
-                        new File(p.getBaseCodePath())));
+                        new File(p.getBaseApkPath())));
             }
         });
         return overlays;
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index e5c8450..dd1978e 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -45,8 +45,8 @@
     private long mBeginTime = UNKNOWN_TIMESTAMP;
     private long mEndTime = UNKNOWN_TIMESTAMP;
     private boolean mShouldTriggerTrace;
-    private long mTotalFramesCount = 0;
-    private long mMissedFramesCount = 0;
+    private int mTotalFramesCount = 0;
+    private int mMissedFramesCount = 0;
     private long mMaxFrameTimeNanos = 0;
 
     private Session mSession;
@@ -124,6 +124,15 @@
         if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime) {
             // The tracing has been ended, remove the observer, see if need to trigger perfetto.
             mRendererWrapper.removeObserver(mObserver);
+
+            // Log the frame stats as counters to make them easily accessible in traces.
+            Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedFrames",
+                    mMissedFramesCount);
+            Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
+                    mTotalFramesCount);
+            Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
+                    (int) (mMaxFrameTimeNanos / 1_000_000));
+
             // Trigger perfetto if necessary.
             if (mShouldTriggerTrace) {
                 if (DEBUG) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8498151..e58990e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -73,6 +73,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.KeyValueListParser;
 import android.util.Log;
@@ -256,7 +257,7 @@
     @GuardedBy("this")
     private long mNumBatchedSingleUidCpuTimeReads;
     @GuardedBy("this")
-    private long mCpuTimeReadsTrackingStartTime = SystemClock.uptimeMillis();
+    private long mCpuTimeReadsTrackingStartTimeMs = SystemClock.uptimeMillis();
     @GuardedBy("this")
     private int mNumUidsRemoved;
     @GuardedBy("this")
@@ -290,7 +291,7 @@
     public final class UidToRemove {
         int startUid;
         int endUid;
-        long timeAddedInQueue;
+        long mTimeAddedInQueueMs;
 
         /** Remove just one UID */
         public UidToRemove(int uid, long timestamp) {
@@ -301,7 +302,7 @@
         public UidToRemove(int startUid, int endUid, long timestamp) {
             this.startUid = startUid;
             this.endUid = endUid;
-            timeAddedInQueue = timestamp;
+            mTimeAddedInQueueMs = timestamp;
         }
 
         void remove() {
@@ -382,9 +383,9 @@
                 }
                 boolean changed = setChargingLocked(true);
                 if (changed) {
-                    final long uptime = mClocks.uptimeMillis();
-                    final long elapsedRealtime = mClocks.elapsedRealtime();
-                    addHistoryRecordLocked(elapsedRealtime, uptime);
+                    final long uptimeMs = mClocks.uptimeMillis();
+                    final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+                    addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
                 }
             }
         }
@@ -502,9 +503,9 @@
     }
 
     public void clearPendingRemovedUids() {
-        long cutOffTime = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
+        long cutOffTimeMs = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
         while (!mPendingRemovedUids.isEmpty()
-                && mPendingRemovedUids.peek().timeAddedInQueue < cutOffTime) {
+                && mPendingRemovedUids.peek().mTimeAddedInQueueMs < cutOffTimeMs) {
             mPendingRemovedUids.poll().remove();
         }
     }
@@ -572,7 +573,7 @@
     }
 
     @VisibleForTesting
-    public long[] addCpuTimes(long[] timesA, long[] timesB) {
+    public static long[] addCpuTimes(long[] timesA, long[] timesB) {
         if (timesA != null && timesB != null) {
             for (int i = timesA.length - 1; i >= 0; --i) {
                 timesA[i] += timesB[i];
@@ -700,7 +701,7 @@
 
     final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
 
-    long mHistoryBaseTime;
+    long mHistoryBaseTimeMs;
     protected boolean mHaveBatteryLevel = false;
     protected boolean mRecordingHistory = false;
     int mNumHistoryItems;
@@ -719,9 +720,9 @@
     int mHistoryBufferLastPos = -1;
     int mActiveHistoryStates = 0xffffffff;
     int mActiveHistoryStates2 = 0xffffffff;
-    long mLastHistoryElapsedRealtime = 0;
-    long mTrackRunningHistoryElapsedRealtime = 0;
-    long mTrackRunningHistoryUptime = 0;
+    long mLastHistoryElapsedRealtimeMs = 0;
+    long mTrackRunningHistoryElapsedRealtimeMs = 0;
+    long mTrackRunningHistoryUptimeMs = 0;
 
     final BatteryStatsHistory mBatteryStatsHistory;
 
@@ -742,28 +743,28 @@
     /**
      * Total time (in milliseconds) spent executing in user code.
      */
-    long mLastStepCpuUserTime;
-    long mCurStepCpuUserTime;
+    long mLastStepCpuUserTimeMs;
+    long mCurStepCpuUserTimeMs;
     /**
      * Total time (in milliseconds) spent executing in kernel code.
      */
-    long mLastStepCpuSystemTime;
-    long mCurStepCpuSystemTime;
+    long mLastStepCpuSystemTimeMs;
+    long mCurStepCpuSystemTimeMs;
     /**
      * Times from /proc/stat (but measured in milliseconds).
      */
-    long mLastStepStatUserTime;
-    long mLastStepStatSystemTime;
-    long mLastStepStatIOWaitTime;
-    long mLastStepStatIrqTime;
-    long mLastStepStatSoftIrqTime;
-    long mLastStepStatIdleTime;
-    long mCurStepStatUserTime;
-    long mCurStepStatSystemTime;
-    long mCurStepStatIOWaitTime;
-    long mCurStepStatIrqTime;
-    long mCurStepStatSoftIrqTime;
-    long mCurStepStatIdleTime;
+    long mLastStepStatUserTimeMs;
+    long mLastStepStatSystemTimeMs;
+    long mLastStepStatIOWaitTimeMs;
+    long mLastStepStatIrqTimeMs;
+    long mLastStepStatSoftIrqTimeMs;
+    long mLastStepStatIdleTimeMs;
+    long mCurStepStatUserTimeMs;
+    long mCurStepStatSystemTimeMs;
+    long mCurStepStatIOWaitTimeMs;
+    long mCurStepStatIrqTimeMs;
+    long mCurStepStatSoftIrqTimeMs;
+    long mCurStepStatIdleTimeMs;
 
     private HistoryItem mHistoryIterator;
     private boolean mReadOverflow;
@@ -771,14 +772,14 @@
 
     int mStartCount;
 
-    long mStartClockTime;
+    long mStartClockTimeMs;
     String mStartPlatformVersion;
     String mEndPlatformVersion;
 
-    long mUptime;
-    long mUptimeStart;
-    long mRealtime;
-    long mRealtimeStart;
+    long mUptimeUs;
+    long mUptimeStartUs;
+    long mRealtimeUs;
+    long mRealtimeStartUs;
 
     int mWakeLockNesting;
     boolean mWakeLockImportant;
@@ -810,9 +811,9 @@
     StopwatchTimer mDeviceLightIdlingTimer;
 
     int mDeviceIdleMode;
-    long mLastIdleTimeStart;
-    long mLongestLightIdleTime;
-    long mLongestFullIdleTime;
+    long mLastIdleTimeStartMs;
+    long mLongestLightIdleTimeMs;
+    long mLongestFullIdleTimeMs;
     StopwatchTimer mDeviceIdleModeLightTimer;
     StopwatchTimer mDeviceIdleModeFullTimer;
 
@@ -921,7 +922,7 @@
     protected StopwatchTimer mBluetoothScanTimer;
 
     int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
-    long mMobileRadioActiveStartTime;
+    long mMobileRadioActiveStartTimeMs;
     StopwatchTimer mMobileRadioActiveTimer;
     StopwatchTimer mMobileRadioActivePerAppTimer;
     LongSamplingCounter mMobileRadioActiveAdjustedTime;
@@ -989,13 +990,13 @@
 
     static final int MAX_DAILY_ITEMS = 10;
 
-    long mDailyStartTime = 0;
-    long mNextMinDailyDeadline = 0;
-    long mNextMaxDailyDeadline = 0;
+    long mDailyStartTimeMs = 0;
+    long mNextMinDailyDeadlineMs = 0;
+    long mNextMaxDailyDeadlineMs = 0;
 
     final ArrayList<DailyItem> mDailyItems = new ArrayList<>();
 
-    long mLastWriteTime = 0; // Milliseconds
+    long mLastWriteTimeMs = 0; // Milliseconds
 
     private int mPhoneServiceState = -1;
     private int mPhoneServiceStateRaw = -1;
@@ -1131,8 +1132,8 @@
      * TimeBase observer.
      */
     public interface TimeBaseObs {
-        void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime);
-        void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime);
+        void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs);
+        void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs);
 
         /**
          * Reset the observer's state, returns true if the timer/counter is inactive
@@ -1140,7 +1141,18 @@
          * @param detachIfReset detach if true, no-op if false.
          * @return Returns true if the timer/counter is inactive and can be destroyed.
          */
-        boolean reset(boolean detachIfReset);
+        default boolean reset(boolean detachIfReset) {
+            return reset(detachIfReset, SystemClock.elapsedRealtime() * 1000);
+        }
+
+        /**
+         * @see #reset(boolean)
+         * @param detachIfReset detach if true, no-op if false.
+         * @param elapsedRealtimeUs the timestamp when this reset is actually reequested
+         * @return Returns true if the timer/counter is inactive and can be destroyed.
+         */
+        boolean reset(boolean detachIfReset, long elapsedRealtimeUs);
+
         /**
          * Detach the observer from TimeBase.
          */
@@ -1150,17 +1162,19 @@
     // methods are protected not private to be VisibleForTesting
     public static class TimeBase {
         protected final Collection<TimeBaseObs> mObservers;
-        protected long mUptime;
-        protected long mRealtime;
+
+        // All below time metrics are in microseconds.
+        protected long mUptimeUs;
+        protected long mRealtimeUs;
 
         protected boolean mRunning;
 
-        protected long mPastUptime;
-        protected long mUptimeStart;
-        protected long mPastRealtime;
-        protected long mRealtimeStart;
-        protected long mUnpluggedUptime;
-        protected long mUnpluggedRealtime;
+        protected long mPastUptimeUs;
+        protected long mUptimeStartUs;
+        protected long mPastRealtimeUs;
+        protected long mRealtimeStartUs;
+        protected long mUnpluggedUptimeUs;
+        protected long mUnpluggedRealtimeUs;
 
         public void dump(PrintWriter pw, String prefix) {
             StringBuilder sb = new StringBuilder(128);
@@ -1168,26 +1182,26 @@
             sb.setLength(0);
             sb.append(prefix);
                     sb.append("mUptime=");
-                    formatTimeMs(sb, mUptime / 1000);
+                    formatTimeMs(sb, mUptimeUs / 1000);
             pw.println(sb.toString());
             sb.setLength(0);
             sb.append(prefix);
                     sb.append("mRealtime=");
-                    formatTimeMs(sb, mRealtime / 1000);
+                    formatTimeMs(sb, mRealtimeUs / 1000);
             pw.println(sb.toString());
             sb.setLength(0);
             sb.append(prefix);
                     sb.append("mPastUptime=");
-                    formatTimeMs(sb, mPastUptime / 1000); sb.append("mUptimeStart=");
-                    formatTimeMs(sb, mUptimeStart / 1000);
-                    sb.append("mUnpluggedUptime="); formatTimeMs(sb, mUnpluggedUptime / 1000);
+                    formatTimeMs(sb, mPastUptimeUs / 1000); sb.append("mUptimeStart=");
+                    formatTimeMs(sb, mUptimeStartUs / 1000);
+                    sb.append("mUnpluggedUptime="); formatTimeMs(sb, mUnpluggedUptimeUs / 1000);
             pw.println(sb.toString());
             sb.setLength(0);
             sb.append(prefix);
                     sb.append("mPastRealtime=");
-                    formatTimeMs(sb, mPastRealtime / 1000); sb.append("mRealtimeStart=");
-                    formatTimeMs(sb, mRealtimeStart / 1000);
-                    sb.append("mUnpluggedRealtime="); formatTimeMs(sb, mUnpluggedRealtime / 1000);
+                    formatTimeMs(sb, mPastRealtimeUs / 1000); sb.append("mRealtimeStart=");
+                    formatTimeMs(sb, mRealtimeStartUs / 1000);
+                    sb.append("mUnpluggedRealtime="); formatTimeMs(sb, mUnpluggedRealtimeUs / 1000);
             pw.println(sb.toString());
         }
         /**
@@ -1219,94 +1233,96 @@
             return mObservers.contains(observer);
         }
 
-        public void init(long uptime, long realtime) {
-            mRealtime = 0;
-            mUptime = 0;
-            mPastUptime = 0;
-            mPastRealtime = 0;
-            mUptimeStart = uptime;
-            mRealtimeStart = realtime;
-            mUnpluggedUptime = getUptime(mUptimeStart);
-            mUnpluggedRealtime = getRealtime(mRealtimeStart);
+        public void init(long uptimeUs, long elapsedRealtimeUs) {
+            mRealtimeUs = 0;
+            mUptimeUs = 0;
+            mPastUptimeUs = 0;
+            mPastRealtimeUs = 0;
+            mUptimeStartUs = uptimeUs;
+            mRealtimeStartUs = elapsedRealtimeUs;
+            mUnpluggedUptimeUs = getUptime(mUptimeStartUs);
+            mUnpluggedRealtimeUs = getRealtime(mRealtimeStartUs);
         }
 
-        public void reset(long uptime, long realtime) {
+        public void reset(long uptimeUs, long elapsedRealtimeUs) {
             if (!mRunning) {
-                mPastUptime = 0;
-                mPastRealtime = 0;
+                mPastUptimeUs = 0;
+                mPastRealtimeUs = 0;
             } else {
-                mUptimeStart = uptime;
-                mRealtimeStart = realtime;
-                // TODO: Since mUptimeStart was just reset and we are running, getUptime will
-                // just return mPastUptime. Also, are we sure we don't want to reset that?
-                mUnpluggedUptime = getUptime(uptime);
+                mUptimeStartUs = uptimeUs;
+                mRealtimeStartUs = elapsedRealtimeUs;
+                // TODO: Since mUptimeStartUs was just reset and we are running, getUptime will
+                // just return mPastUptimeUs. Also, are we sure we don't want to reset that?
+                mUnpluggedUptimeUs = getUptime(uptimeUs);
                 // TODO: likewise.
-                mUnpluggedRealtime = getRealtime(realtime);
+                mUnpluggedRealtimeUs = getRealtime(elapsedRealtimeUs);
             }
         }
 
-        public long computeUptime(long curTime, int which) {
-            return mUptime + getUptime(curTime);
+        public long computeUptime(long curTimeUs, int which) {
+            return mUptimeUs + getUptime(curTimeUs);
         }
 
-        public long computeRealtime(long curTime, int which) {
-            return mRealtime + getRealtime(curTime);
+        public long computeRealtime(long curTimeUs, int which) {
+            return mRealtimeUs + getRealtime(curTimeUs);
         }
 
-        public long getUptime(long curTime) {
-            long time = mPastUptime;
+        public long getUptime(long curTimeUs) {
+            long time = mPastUptimeUs;
             if (mRunning) {
-                time += curTime - mUptimeStart;
+                time += curTimeUs - mUptimeStartUs;
             }
             return time;
         }
 
-        public long getRealtime(long curTime) {
-            long time = mPastRealtime;
+        public long getRealtime(long curTimeUs) {
+            long time = mPastRealtimeUs;
             if (mRunning) {
-                time += curTime - mRealtimeStart;
+                time += curTimeUs - mRealtimeStartUs;
             }
             return time;
         }
 
         public long getUptimeStart() {
-            return mUptimeStart;
+            return mUptimeStartUs;
         }
 
         public long getRealtimeStart() {
-            return mRealtimeStart;
+            return mRealtimeStartUs;
         }
 
         public boolean isRunning() {
             return mRunning;
         }
 
-        public boolean setRunning(boolean running, long uptime, long realtime) {
+        public boolean setRunning(boolean running, long uptimeUs, long elapsedRealtimeUs) {
             if (mRunning != running) {
                 mRunning = running;
                 if (running) {
-                    mUptimeStart = uptime;
-                    mRealtimeStart = realtime;
-                    long batteryUptime = mUnpluggedUptime = getUptime(uptime);
-                    long batteryRealtime = mUnpluggedRealtime = getRealtime(realtime);
+                    mUptimeStartUs = uptimeUs;
+                    mRealtimeStartUs = elapsedRealtimeUs;
+                    long batteryUptimeUs = mUnpluggedUptimeUs = getUptime(uptimeUs);
+                    long batteryRealtimeUs = mUnpluggedRealtimeUs = getRealtime(elapsedRealtimeUs);
                     // Normally we do not use Iterator in framework code to avoid alloc/dealloc
                     // Iterator object, here is an exception because mObservers' type is Collection
                     // instead of list.
                     final Iterator<TimeBaseObs> iter = mObservers.iterator();
                     while (iter.hasNext()) {
-                        iter.next().onTimeStarted(realtime, batteryUptime, batteryRealtime);
+                        iter.next().onTimeStarted(
+                                elapsedRealtimeUs, batteryUptimeUs, batteryRealtimeUs);
                     }
                 } else {
-                    mPastUptime += uptime - mUptimeStart;
-                    mPastRealtime += realtime - mRealtimeStart;
-                    long batteryUptime = getUptime(uptime);
-                    long batteryRealtime = getRealtime(realtime);
+                    mPastUptimeUs += uptimeUs - mUptimeStartUs;
+                    mPastRealtimeUs += elapsedRealtimeUs - mRealtimeStartUs;
+                    long batteryUptimeUs = getUptime(uptimeUs);
+                    long batteryRealtimeUs = getRealtime(elapsedRealtimeUs);
                     // Normally we do not use Iterator in framework code to avoid alloc/dealloc
                     // Iterator object, here is an exception because mObservers' type is Collection
                     // instead of list.
                     final Iterator<TimeBaseObs> iter = mObservers.iterator();
                     while (iter.hasNext()) {
-                        iter.next().onTimeStopped(realtime, batteryUptime, batteryRealtime);
+                        iter.next().onTimeStopped(
+                                elapsedRealtimeUs, batteryUptimeUs, batteryRealtimeUs);
                     }
                 }
                 return true;
@@ -1315,38 +1331,38 @@
         }
 
         public void readSummaryFromParcel(Parcel in) {
-            mUptime = in.readLong();
-            mRealtime = in.readLong();
+            mUptimeUs = in.readLong();
+            mRealtimeUs = in.readLong();
         }
 
-        public void writeSummaryToParcel(Parcel out, long uptime, long realtime) {
-            out.writeLong(computeUptime(uptime, STATS_SINCE_CHARGED));
-            out.writeLong(computeRealtime(realtime, STATS_SINCE_CHARGED));
+        public void writeSummaryToParcel(Parcel out, long uptimeUs, long elapsedRealtimeUs) {
+            out.writeLong(computeUptime(uptimeUs, STATS_SINCE_CHARGED));
+            out.writeLong(computeRealtime(elapsedRealtimeUs, STATS_SINCE_CHARGED));
         }
 
         public void readFromParcel(Parcel in) {
             mRunning = false;
-            mUptime = in.readLong();
-            mPastUptime = in.readLong();
-            mUptimeStart = in.readLong();
-            mRealtime = in.readLong();
-            mPastRealtime = in.readLong();
-            mRealtimeStart = in.readLong();
-            mUnpluggedUptime = in.readLong();
-            mUnpluggedRealtime = in.readLong();
+            mUptimeUs = in.readLong();
+            mPastUptimeUs = in.readLong();
+            mUptimeStartUs = in.readLong();
+            mRealtimeUs = in.readLong();
+            mPastRealtimeUs = in.readLong();
+            mRealtimeStartUs = in.readLong();
+            mUnpluggedUptimeUs = in.readLong();
+            mUnpluggedRealtimeUs = in.readLong();
         }
 
-        public void writeToParcel(Parcel out, long uptime, long realtime) {
-            final long runningUptime = getUptime(uptime);
-            final long runningRealtime = getRealtime(realtime);
-            out.writeLong(mUptime);
+        public void writeToParcel(Parcel out, long uptimeUs, long elapsedRealtimeUs) {
+            final long runningUptime = getUptime(uptimeUs);
+            final long runningRealtime = getRealtime(elapsedRealtimeUs);
+            out.writeLong(mUptimeUs);
             out.writeLong(runningUptime);
-            out.writeLong(mUptimeStart);
-            out.writeLong(mRealtime);
+            out.writeLong(mUptimeStartUs);
+            out.writeLong(mRealtimeUs);
             out.writeLong(runningRealtime);
-            out.writeLong(mRealtimeStart);
-            out.writeLong(mUnpluggedUptime);
-            out.writeLong(mUnpluggedRealtime);
+            out.writeLong(mRealtimeStartUs);
+            out.writeLong(mUnpluggedUptimeUs);
+            out.writeLong(mUnpluggedRealtimeUs);
         }
     }
 
@@ -1374,11 +1390,11 @@
         }
 
         @Override
-        public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
         }
 
         @Override
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
         }
 
         /**
@@ -1436,7 +1452,7 @@
          * Clear state of this counter.
          */
         @Override
-        public boolean reset(boolean detachIfReset) {
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
             mCount.set(0);
             if (detachIfReset) {
                 detach();
@@ -1481,11 +1497,11 @@
         }
 
         @Override
-        public void onTimeStarted(long elapsedRealTime, long baseUptime, long baseRealtime) {
+        public void onTimeStarted(long elapsedRealTimeUs, long baseUptimeUs, long baseRealtimeUs) {
         }
 
         @Override
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
         }
 
         @Override
@@ -1524,7 +1540,7 @@
          * Clear state of this counter.
          */
         @Override
-        public boolean reset(boolean detachIfReset) {
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
             if (mCounts != null) {
                 Arrays.fill(mCounts, 0);
             }
@@ -1608,11 +1624,11 @@
         }
 
         @Override
-        public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
         }
 
         @Override
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
         }
 
         public long getCountLocked(int which) {
@@ -1638,7 +1654,7 @@
          * Clear state of this counter.
          */
         @Override
-        public boolean reset(boolean detachIfReset) {
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
             mCount = 0;
             if (detachIfReset) {
                 detach();
@@ -1678,13 +1694,13 @@
          * boot, to the last time something interesting happened in the
          * current run.
          */
-        protected long mTotalTime;
+        protected long mTotalTimeUs;
 
         /**
          * The total time this timer has been running until the latest mark has been set.
-         * Subtract this from mTotalTime to get the time spent running since the mark was set.
+         * Subtract this from mTotalTimeUs to get the time spent running since the mark was set.
          */
-        protected long mTimeBeforeMark;
+        protected long mTimeBeforeMarkUs;
 
         /**
          * Constructs from a parcel.
@@ -1698,10 +1714,10 @@
             mTimeBase = timeBase;
 
             mCount = in.readInt();
-            mTotalTime = in.readLong();
-            mTimeBeforeMark = in.readLong();
+            mTotalTimeUs = in.readLong();
+            mTimeBeforeMarkUs = in.readLong();
             timeBase.add(this);
-            if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
+            if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTimeUs);
         }
 
         public Timer(Clocks clocks, int type, TimeBase timeBase) {
@@ -1714,14 +1730,17 @@
         public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
             if (DEBUG) {
                 Log.i(TAG, "**** WRITING TIMER #" + mType + ": mTotalTime="
-                        + computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
+                        + computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
+                                elapsedRealtimeUs));
             }
             out.writeInt(computeCurrentCountLocked());
-            out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
-            out.writeLong(mTimeBeforeMark);
+            out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
+                        elapsedRealtimeUs));
+            out.writeLong(mTimeBeforeMarkUs);
         }
 
-        protected abstract long computeRunTimeLocked(long curBatteryRealtime);
+        protected abstract long computeRunTimeLocked(long curBatteryRealtime,
+                long elapsedRealtimeUs);
 
         protected abstract int computeCurrentCountLocked();
 
@@ -1731,7 +1750,12 @@
          */
         @Override
         public boolean reset(boolean detachIfReset) {
-            mTotalTime = mTimeBeforeMark = 0;
+            return reset(detachIfReset, mClocks.elapsedRealtime() * 1000);
+        }
+
+        @Override
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
+            mTotalTimeUs = mTimeBeforeMarkUs = 0;
             mCount = 0;
             if (detachIfReset) {
                 detach();
@@ -1745,19 +1769,20 @@
         }
 
         @Override
-        public void onTimeStarted(long elapsedRealtime, long timeBaseUptime, long baseRealtime) {
+        public void onTimeStarted(long elapsedRealtimeUs, long timeBaseUptimeUs,
+                long baseRealtimeUs) {
         }
 
         @Override
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
             if (DEBUG && mType < 0) {
-                Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtime
-                        + " old mTotalTime=" + mTotalTime);
+                Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtimeUs
+                        + " old mTotalTime=" + mTotalTimeUs);
             }
-            mTotalTime = computeRunTimeLocked(baseRealtime);
+            mTotalTimeUs = computeRunTimeLocked(baseRealtimeUs, elapsedRealtimeUs);
             mCount = computeCurrentCountLocked();
             if (DEBUG && mType < 0) {
-                Log.v(TAG, "plug #" + mType + ": new mTotalTime=" + mTotalTime);
+                Log.v(TAG, "plug #" + mType + ": new mTotalTime=" + mTotalTimeUs);
             }
         }
 
@@ -1780,7 +1805,8 @@
         @Override
         @UnsupportedAppUsage
         public long getTotalTimeLocked(long elapsedRealtimeUs, int which) {
-            return computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
+            return computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
+                    elapsedRealtimeUs);
         }
 
         @Override
@@ -1791,29 +1817,31 @@
 
         @Override
         public long getTimeSinceMarkLocked(long elapsedRealtimeUs) {
-            long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
-            return val - mTimeBeforeMark;
+            long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
+                    elapsedRealtimeUs);
+            return val - mTimeBeforeMarkUs;
         }
 
         @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCount=" + mCount);
-            pw.println(prefix + "mTotalTime=" + mTotalTime);
+            pw.println(prefix + "mTotalTime=" + mTotalTimeUs);
         }
 
 
         public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
-            long runTime = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
-            out.writeLong(runTime);
+            long runTimeUs = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs),
+                    elapsedRealtimeUs);
+            out.writeLong(runTimeUs);
             out.writeInt(computeCurrentCountLocked());
         }
 
         public void readSummaryFromParcelLocked(Parcel in) {
             // Multiply by 1000 for backwards compatibility
-            mTotalTime = in.readLong();
+            mTotalTimeUs = in.readLong();
             mCount = in.readInt();
             // When reading the summary, we set the mark to be the latest information.
-            mTimeBeforeMark = mTotalTime;
+            mTimeBeforeMarkUs = mTotalTimeUs;
         }
     }
 
@@ -1843,14 +1871,14 @@
         /**
          * The most recent reported total_time from /proc/wakelocks.
          */
-        long mCurrentReportedTotalTime;
+        long mCurrentReportedTotalTimeUs;
 
 
         /**
          * The reported total_time from /proc/wakelocks when unplug() was last
          * called.
          */
-        long mUnpluggedReportedTotalTime;
+        long mUnpluggedReportedTotalTimeUs;
 
         /**
          * Whether we are currently in a discharge cycle.
@@ -1872,8 +1900,8 @@
             super(clocks, 0, timeBase, in);
             mCurrentReportedCount = in.readInt();
             mUnpluggedReportedCount = in.readInt();
-            mCurrentReportedTotalTime = in.readLong();
-            mUnpluggedReportedTotalTime = in.readLong();
+            mCurrentReportedTotalTimeUs = in.readLong();
+            mUnpluggedReportedTotalTimeUs = in.readLong();
             mTrackingReportedValues = in.readInt() == 1;
             mTimeBaseRunning = timeBase.isRunning();
         }
@@ -1890,9 +1918,16 @@
          * be less than the values used for a previous invocation.
          */
         public void endSample() {
-            mTotalTime = computeRunTimeLocked(0 /* unused by us */);
+            endSample(mClocks.elapsedRealtime() * 1000);
+        }
+
+        /**
+         * @see #endSample()
+         */
+        public void endSample(long elapsedRealtimeUs) {
+            mTotalTimeUs = computeRunTimeLocked(0 /* unused by us */, elapsedRealtimeUs);
             mCount = computeCurrentCountLocked();
-            mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = 0;
+            mUnpluggedReportedTotalTimeUs = mCurrentReportedTotalTimeUs = 0;
             mUnpluggedReportedCount = mCurrentReportedCount = 0;
             mTrackingReportedValues = false;
         }
@@ -1911,26 +1946,33 @@
          *
          * If the values being recorded have been reset, the monotonically increasing requirement
          * will be broken. In this case, {@link #endSample()} is automatically called and
-         * the total value of totalTime and count are recorded, starting a new monotonically
+         * the total value of totalTimeUs and count are recorded, starting a new monotonically
          * increasing sample.
          *
-         * @param totalTime total time of sample in microseconds.
+         * @param totalTimeUs total time of sample in microseconds.
          * @param count total number of times the event being sampled occurred.
          */
-        public void update(long totalTime, int count) {
+        public void updated(long totalTimeUs, int count) {
+            update(totalTimeUs, count, mClocks.elapsedRealtime() * 1000);
+        }
+
+        /**
+         * @see #update(long, int)
+         */
+        public void update(long totalTimeUs, int count, long elapsedRealtimeUs) {
             if (mTimeBaseRunning && !mTrackingReportedValues) {
                 // Updating the reported value for the first time.
-                mUnpluggedReportedTotalTime = totalTime;
+                mUnpluggedReportedTotalTimeUs = totalTimeUs;
                 mUnpluggedReportedCount = count;
             }
 
             mTrackingReportedValues = true;
 
-            if (totalTime < mCurrentReportedTotalTime || count < mCurrentReportedCount) {
-                endSample();
+            if (totalTimeUs < mCurrentReportedTotalTimeUs || count < mCurrentReportedCount) {
+                endSample(elapsedRealtimeUs);
             }
 
-            mCurrentReportedTotalTime = totalTime;
+            mCurrentReportedTotalTimeUs = totalTimeUs;
             mCurrentReportedCount = count;
         }
 
@@ -1940,23 +1982,31 @@
          * @param deltaTime additional time recorded since the last sampled event, in microseconds.
          * @param deltaCount additional number of times the event being sampled occurred.
          */
-        public void add(long deltaTime, int deltaCount) {
-            update(mCurrentReportedTotalTime + deltaTime, mCurrentReportedCount + deltaCount);
+        public void add(long deltaTimeUs, int deltaCount) {
+            add(deltaTimeUs, deltaCount, mClocks.elapsedRealtime() * 1000);
+        }
+
+        /**
+         * @see #add(long, int)
+         */
+        public void add(long deltaTimeUs, int deltaCount, long elapsedRealtimeUs) {
+            update(mCurrentReportedTotalTimeUs + deltaTimeUs, mCurrentReportedCount + deltaCount,
+                    elapsedRealtimeUs);
         }
 
         @Override
-        public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
-            super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
+        public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+            super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
             if (mTrackingReportedValues) {
-                mUnpluggedReportedTotalTime = mCurrentReportedTotalTime;
+                mUnpluggedReportedTotalTimeUs = mCurrentReportedTotalTimeUs;
                 mUnpluggedReportedCount = mCurrentReportedCount;
             }
             mTimeBaseRunning = true;
         }
 
         @Override
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
-            super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+            super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
             mTimeBaseRunning = false;
         }
 
@@ -1965,14 +2015,14 @@
             super.logState(pw, prefix);
             pw.println(prefix + "mCurrentReportedCount=" + mCurrentReportedCount
                     + " mUnpluggedReportedCount=" + mUnpluggedReportedCount
-                    + " mCurrentReportedTotalTime=" + mCurrentReportedTotalTime
-                    + " mUnpluggedReportedTotalTime=" + mUnpluggedReportedTotalTime);
+                    + " mCurrentReportedTotalTime=" + mCurrentReportedTotalTimeUs
+                    + " mUnpluggedReportedTotalTime=" + mUnpluggedReportedTotalTimeUs);
         }
 
         @Override
-        protected long computeRunTimeLocked(long curBatteryRealtime) {
-            return mTotalTime + (mTimeBaseRunning && mTrackingReportedValues
-                    ? mCurrentReportedTotalTime - mUnpluggedReportedTotalTime : 0);
+        protected long computeRunTimeLocked(long curBatteryRealtime, long elapsedRealtimeUs) {
+            return mTotalTimeUs + (mTimeBaseRunning && mTrackingReportedValues
+                    ? mCurrentReportedTotalTimeUs - mUnpluggedReportedTotalTimeUs : 0);
         }
 
         @Override
@@ -1986,16 +2036,16 @@
             super.writeToParcel(out, elapsedRealtimeUs);
             out.writeInt(mCurrentReportedCount);
             out.writeInt(mUnpluggedReportedCount);
-            out.writeLong(mCurrentReportedTotalTime);
-            out.writeLong(mUnpluggedReportedTotalTime);
+            out.writeLong(mCurrentReportedTotalTimeUs);
+            out.writeLong(mUnpluggedReportedTotalTimeUs);
             out.writeInt(mTrackingReportedValues ? 1 : 0);
         }
 
         @Override
-        public boolean reset(boolean detachIfReset) {
-            super.reset(detachIfReset);
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
+            super.reset(detachIfReset, elapsedRealtimeUs);
             mTrackingReportedValues = false;
-            mUnpluggedReportedTotalTime = 0;
+            mUnpluggedReportedTotalTimeUs = 0;
             mUnpluggedReportedCount = 0;
             return true;
         }
@@ -2011,12 +2061,12 @@
         /**
          * The last time at which we updated the timer.  This is in elapsed realtime microseconds.
          */
-        long mLastAddedTime;
+        long mLastAddedTimeUs;
 
         /**
          * The last duration that we added to the timer.  This is in microseconds.
          */
-        long mLastAddedDuration;
+        long mLastAddedDurationUs;
 
         /**
          * Whether we are currently in a discharge cycle.
@@ -2026,8 +2076,8 @@
         BatchTimer(Clocks clocks, Uid uid, int type, TimeBase timeBase, Parcel in) {
             super(clocks, type, timeBase, in);
             mUid = uid;
-            mLastAddedTime = in.readLong();
-            mLastAddedDuration = in.readLong();
+            mLastAddedTimeUs = in.readLong();
+            mLastAddedDurationUs = in.readLong();
             mInDischarge = timeBase.isRunning();
         }
 
@@ -2040,74 +2090,82 @@
         @Override
         public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
             super.writeToParcel(out, elapsedRealtimeUs);
-            out.writeLong(mLastAddedTime);
-            out.writeLong(mLastAddedDuration);
+            out.writeLong(mLastAddedTimeUs);
+            out.writeLong(mLastAddedDurationUs);
         }
 
         @Override
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
-            recomputeLastDuration(mClocks.elapsedRealtime() * 1000, false);
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+            recomputeLastDuration(elapsedRealtimeUs, false);
             mInDischarge = false;
-            super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
+            super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
         }
 
         @Override
-        public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
-            recomputeLastDuration(elapsedRealtime, false);
+        public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+            recomputeLastDuration(elapsedRealtimeUs, false);
             mInDischarge = true;
             // If we are still within the last added duration, then re-added whatever remains.
-            if (mLastAddedTime == elapsedRealtime) {
-                mTotalTime += mLastAddedDuration;
+            if (mLastAddedTimeUs == elapsedRealtimeUs) {
+                mTotalTimeUs += mLastAddedDurationUs;
             }
-            super.onTimeStarted(elapsedRealtime, baseUptime, baseRealtime);
+            super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
         }
 
         @Override
         public void logState(Printer pw, String prefix) {
             super.logState(pw, prefix);
-            pw.println(prefix + "mLastAddedTime=" + mLastAddedTime
-                    + " mLastAddedDuration=" + mLastAddedDuration);
+            pw.println(prefix + "mLastAddedTime=" + mLastAddedTimeUs
+                    + " mLastAddedDuration=" + mLastAddedDurationUs);
         }
 
-        private long computeOverage(long curTime) {
-            if (mLastAddedTime > 0) {
-                return mLastAddedDuration - curTime;
+        private long computeOverage(long curTimeUs) {
+            if (mLastAddedTimeUs > 0) {
+                return mLastAddedDurationUs - curTimeUs;
             }
             return 0;
         }
 
-        private void recomputeLastDuration(long curTime, boolean abort) {
-            final long overage = computeOverage(curTime);
+        private void recomputeLastDuration(long curTimeUs, boolean abort) {
+            final long overage = computeOverage(curTimeUs);
             if (overage > 0) {
                 // Aborting before the duration ran out -- roll back the remaining
                 // duration.  Only do this if currently discharging; otherwise we didn't
                 // actually add the time.
                 if (mInDischarge) {
-                    mTotalTime -= overage;
+                    mTotalTimeUs -= overage;
                 }
                 if (abort) {
-                    mLastAddedTime = 0;
+                    mLastAddedTimeUs = 0;
                 } else {
-                    mLastAddedTime = curTime;
-                    mLastAddedDuration -= overage;
+                    mLastAddedTimeUs = curTimeUs;
+                    mLastAddedDurationUs -= overage;
                 }
             }
         }
 
-        public void addDuration(BatteryStatsImpl stats, long durationMillis) {
-            final long now = mClocks.elapsedRealtime() * 1000;
-            recomputeLastDuration(now, true);
-            mLastAddedTime = now;
-            mLastAddedDuration = durationMillis * 1000;
+        public void addDuration(BatteryStatsImpl stats, long durationMs) {
+            addDuration(stats, durationMs, mClocks.elapsedRealtime());
+        }
+
+        public void addDuration(BatteryStatsImpl stats, long durationMs, long elapsedRealtimeMs) {
+            final long nowUs = elapsedRealtimeMs * 1000;
+            recomputeLastDuration(nowUs, true);
+            mLastAddedTimeUs = nowUs;
+            mLastAddedDurationUs = durationMs * 1000;
             if (mInDischarge) {
-                mTotalTime += mLastAddedDuration;
+                mTotalTimeUs += mLastAddedDurationUs;
                 mCount++;
             }
         }
 
         public void abortLastDuration(BatteryStatsImpl stats) {
-            final long now = mClocks.elapsedRealtime() * 1000;
-            recomputeLastDuration(now, true);
+            abortLastDuration(stats, mClocks.elapsedRealtime());
+        }
+
+        public void abortLastDuration(BatteryStatsImpl stats, long elapsedRealtimeMs) {
+            final long nowUs = elapsedRealtimeMs * 1000;
+            recomputeLastDuration(nowUs, true);
         }
 
         @Override
@@ -2116,20 +2174,19 @@
         }
 
         @Override
-        protected long computeRunTimeLocked(long curBatteryRealtime) {
-            final long overage = computeOverage(mClocks.elapsedRealtime() * 1000);
+        protected long computeRunTimeLocked(long curBatteryRealtimeUs, long elapsedRealtimeUs) {
+            final long overage = computeOverage(elapsedRealtimeUs);
             if (overage > 0) {
-                return mTotalTime = overage;
+                return mTotalTimeUs = overage;
             }
-            return mTotalTime;
+            return mTotalTimeUs;
         }
 
         @Override
-        public boolean reset(boolean detachIfReset) {
-            final long now = mClocks.elapsedRealtime() * 1000;
-            recomputeLastDuration(now, true);
-            boolean stillActive = mLastAddedTime == now;
-            super.reset(!stillActive && detachIfReset);
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
+            recomputeLastDuration(elapsedRealtimeUs, true);
+            boolean stillActive = mLastAddedTimeUs == elapsedRealtimeUs;
+            super.reset(!stillActive && detachIfReset, elapsedRealtimeUs);
             return !stillActive;
         }
     }
@@ -2225,10 +2282,10 @@
          *
          * If the timer is also running, store the start time.
          */
-        public void onTimeStarted(long elapsedRealtimeUs, long baseUptime, long baseRealtime) {
-            super.onTimeStarted(elapsedRealtimeUs, baseUptime, baseRealtime);
+        public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+            super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
             if (mNesting > 0) {
-                mStartTimeMs = baseRealtime / 1000;
+                mStartTimeMs = baseRealtimeUs / 1000;
             }
         }
 
@@ -2238,8 +2295,8 @@
          * If the timer is running, add the duration into mCurrentDurationMs.
          */
         @Override
-        public void onTimeStopped(long elapsedRealtimeUs, long baseUptime, long baseRealtimeUs) {
-            super.onTimeStopped(elapsedRealtimeUs, baseUptime, baseRealtimeUs);
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+            super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
             if (mNesting > 0) {
                 // baseRealtimeUs has already been converted to the timebase's realtime.
                 mCurrentDurationMs += (baseRealtimeUs / 1000) - mStartTimeMs;
@@ -2284,13 +2341,13 @@
         }
 
         @Override
-        public boolean reset(boolean detachIfReset) {
-            boolean result = super.reset(detachIfReset);
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
+            boolean result = super.reset(detachIfReset, elapsedRealtimeUs);
             mMaxDurationMs = 0;
             mTotalDurationMs = 0;
             mCurrentDurationMs = 0;
             if (mNesting > 0) {
-                mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime() * 1000) / 1000;
+                mStartTimeMs = mTimeBase.getRealtime(elapsedRealtimeUs) / 1000;
             } else {
                 mStartTimeMs = -1;
             }
@@ -2364,16 +2421,16 @@
          * subtract this from the current battery time to find the amount of
          * time we have been running since we last computed an update.
          */
-        long mUpdateTime;
+        long mUpdateTimeUs;
 
         /**
          * The total time at which the timer was acquired, to determine if it
          * was actually held for an interesting duration. If time base was not running when timer
          * was acquired, will be -1.
          */
-        long mAcquireTime = -1;
+        long mAcquireTimeUs = -1;
 
-        long mTimeout;
+        long mTimeoutUs;
 
         /**
          * For partial wake locks, keep track of whether we are in the list
@@ -2387,7 +2444,7 @@
             super(clocks, type, timeBase, in);
             mUid = uid;
             mTimerPool = timerPool;
-            mUpdateTime = in.readLong();
+            mUpdateTimeUs = in.readLong();
         }
 
         public StopwatchTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
@@ -2397,56 +2454,56 @@
             mTimerPool = timerPool;
         }
 
-        public void setTimeout(long timeout) {
-            mTimeout = timeout;
+        public void setTimeout(long timeoutUs) {
+            mTimeoutUs = timeoutUs;
         }
 
         public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
             super.writeToParcel(out, elapsedRealtimeUs);
-            out.writeLong(mUpdateTime);
+            out.writeLong(mUpdateTimeUs);
         }
 
-        public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+        public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
             if (mNesting > 0) {
                 if (DEBUG && mType < 0) {
-                    Log.v(TAG, "old mUpdateTime=" + mUpdateTime);
+                    Log.v(TAG, "old mUpdateTime=" + mUpdateTimeUs);
                 }
-                super.onTimeStopped(elapsedRealtime, baseUptime, baseRealtime);
-                mUpdateTime = baseRealtime;
+                super.onTimeStopped(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
+                mUpdateTimeUs = baseRealtimeUs;
                 if (DEBUG && mType < 0) {
-                    Log.v(TAG, "new mUpdateTime=" + mUpdateTime);
+                    Log.v(TAG, "new mUpdateTime=" + mUpdateTimeUs);
                 }
             }
         }
 
         public void logState(Printer pw, String prefix) {
             super.logState(pw, prefix);
-            pw.println(prefix + "mNesting=" + mNesting + " mUpdateTime=" + mUpdateTime
-                    + " mAcquireTime=" + mAcquireTime);
+            pw.println(prefix + "mNesting=" + mNesting + " mUpdateTime=" + mUpdateTimeUs
+                    + " mAcquireTime=" + mAcquireTimeUs);
         }
 
         public void startRunningLocked(long elapsedRealtimeMs) {
             if (mNesting++ == 0) {
-                final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
-                mUpdateTime = batteryRealtime;
+                final long batteryRealtimeUs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+                mUpdateTimeUs = batteryRealtimeUs;
                 if (mTimerPool != null) {
                     // Accumulate time to all currently active timers before adding
                     // this new one to the pool.
-                    refreshTimersLocked(batteryRealtime, mTimerPool, null);
+                    refreshTimersLocked(batteryRealtimeUs, mTimerPool, null);
                     // Add this timer to the active pool
                     mTimerPool.add(this);
                 }
                 if (mTimeBase.isRunning()) {
                     // Increment the count
                     mCount++;
-                    mAcquireTime = mTotalTime;
+                    mAcquireTimeUs = mTotalTimeUs;
                 } else {
-                    mAcquireTime = -1;
+                    mAcquireTimeUs = -1;
                 }
                 if (DEBUG && mType < 0) {
-                    Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTime
-                            + " mTotalTime=" + mTotalTime + " mCount=" + mCount
-                            + " mAcquireTime=" + mAcquireTime);
+                    Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTimeUs
+                            + " mTotalTime=" + mTotalTimeUs + " mCount=" + mCount
+                            + " mAcquireTime=" + mAcquireTimeUs);
                 }
             }
         }
@@ -2461,26 +2518,27 @@
                 return;
             }
             if (--mNesting == 0) {
-                final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+                final long batteryRealtimeUs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
                 if (mTimerPool != null) {
                     // Accumulate time to all active counters, scaled by the total
                     // active in the pool, before taking this one out of the pool.
-                    refreshTimersLocked(batteryRealtime, mTimerPool, null);
+                    refreshTimersLocked(batteryRealtimeUs, mTimerPool, null);
                     // Remove this timer from the active pool
                     mTimerPool.remove(this);
                 } else {
                     mNesting = 1;
-                    mTotalTime = computeRunTimeLocked(batteryRealtime);
+                    mTotalTimeUs = computeRunTimeLocked(batteryRealtimeUs,
+                            elapsedRealtimeMs * 1000);
                     mNesting = 0;
                 }
 
                 if (DEBUG && mType < 0) {
-                    Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTime
-                            + " mTotalTime=" + mTotalTime + " mCount=" + mCount
-                            + " mAcquireTime=" + mAcquireTime);
+                    Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTimeUs
+                            + " mTotalTime=" + mTotalTimeUs + " mCount=" + mCount
+                            + " mAcquireTime=" + mAcquireTimeUs);
                 }
 
-                if (mAcquireTime >= 0 && mTotalTime == mAcquireTime) {
+                if (mAcquireTimeUs >= 0 && mTotalTimeUs == mAcquireTimeUs) {
                     // If there was no change in the time, then discard this
                     // count.  A somewhat cheezy strategy, but hey.
                     mCount--;
@@ -2497,32 +2555,32 @@
 
         // Update the total time for all other running Timers with the same type as this Timer
         // due to a change in timer count
-        private static long refreshTimersLocked(long batteryRealtime,
+        private static long refreshTimersLocked(long batteryRealtimeUs,
                 final ArrayList<StopwatchTimer> pool, StopwatchTimer self) {
-            long selfTime = 0;
+            long selfTimeUs = 0;
             final int N = pool.size();
             for (int i=N-1; i>= 0; i--) {
                 final StopwatchTimer t = pool.get(i);
-                long heldTime = batteryRealtime - t.mUpdateTime;
-                if (heldTime > 0) {
-                    final long myTime = heldTime / N;
+                long heldTimeUs = batteryRealtimeUs - t.mUpdateTimeUs;
+                if (heldTimeUs > 0) {
+                    final long myTimeUs = heldTimeUs / N;
                     if (t == self) {
-                        selfTime = myTime;
+                        selfTimeUs = myTimeUs;
                     }
-                    t.mTotalTime += myTime;
+                    t.mTotalTimeUs += myTimeUs;
                 }
-                t.mUpdateTime = batteryRealtime;
+                t.mUpdateTimeUs = batteryRealtimeUs;
             }
-            return selfTime;
+            return selfTimeUs;
         }
 
         @Override
-        protected long computeRunTimeLocked(long curBatteryRealtime) {
-            if (mTimeout > 0 && curBatteryRealtime > mUpdateTime + mTimeout) {
-                curBatteryRealtime = mUpdateTime + mTimeout;
+        protected long computeRunTimeLocked(long curBatteryRealtimeUs, long elapsedRealtimeUs) {
+            if (mTimeoutUs > 0 && curBatteryRealtimeUs > mUpdateTimeUs + mTimeoutUs) {
+                curBatteryRealtimeUs = mUpdateTimeUs + mTimeoutUs;
             }
-            return mTotalTime + (mNesting > 0
-                    ? (curBatteryRealtime - mUpdateTime)
+            return mTotalTimeUs + (mNesting > 0
+                    ? (curBatteryRealtimeUs - mUpdateTimeUs)
                             / (mTimerPool != null ? mTimerPool.size() : 1)
                     : 0);
         }
@@ -2533,13 +2591,14 @@
         }
 
         @Override
-        public boolean reset(boolean detachIfReset) {
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
             boolean canDetach = mNesting <= 0;
-            super.reset(canDetach && detachIfReset);
+            super.reset(canDetach && detachIfReset, elapsedRealtimeUs);
             if (mNesting > 0) {
-                mUpdateTime = mTimeBase.getRealtime(mClocks.elapsedRealtime() * 1000);
+                mUpdateTimeUs = mTimeBase.getRealtime(elapsedRealtimeUs);
             }
-            mAcquireTime = -1; // to ensure mCount isn't decreased to -1 if timer is stopped later.
+            // To ensure mCount isn't decreased to -1 if timer is stopped later.
+            mAcquireTimeUs = -1;
             return canDetach;
         }
 
@@ -2565,17 +2624,17 @@
          * @param elapsedRealtimeMs the current elapsed realtime in milliseconds.
          */
         public void setMark(long elapsedRealtimeMs) {
-            final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+            final long batteryRealtimeUs = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
             if (mNesting > 0) {
                 // We are running.
                 if (mTimerPool != null) {
-                    refreshTimersLocked(batteryRealtime, mTimerPool, this);
+                    refreshTimersLocked(batteryRealtimeUs, mTimerPool, this);
                 } else {
-                    mTotalTime += batteryRealtime - mUpdateTime;
-                    mUpdateTime = batteryRealtime;
+                    mTotalTimeUs += batteryRealtimeUs - mUpdateTimeUs;
+                    mUpdateTimeUs = batteryRealtimeUs;
                 }
             }
-            mTimeBeforeMark = mTotalTime;
+            mTimeBeforeMarkUs = mTotalTimeUs;
         }
     }
 
@@ -2641,11 +2700,11 @@
         }
 
         @Override
-        public boolean reset(boolean detachIfReset) {
+        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
             boolean active = false;
             // Do not detach the subTimer explicitly since that'll be done by DualTimer.detach().
-            active |= !mSubTimer.reset(false);
-            active |= !super.reset(detachIfReset);
+            active |= !mSubTimer.reset(false, elapsedRealtimeUs);
+            active |= !super.reset(detachIfReset, elapsedRealtimeUs);
             return !active;
         }
 
@@ -2682,10 +2741,10 @@
         final ArrayMap<String, T> mMap = new ArrayMap<>();
         T mCurOverflow;
         ArrayMap<String, MutableInt> mActiveOverflow;
-        long mLastOverflowTime;
-        long mLastOverflowFinishTime;
-        long mLastClearTime;
-        long mLastCleanupTime;
+        long mLastOverflowTimeMs;
+        long mLastOverflowFinishTimeMs;
+        long mLastClearTimeMs;
+        long mLastCleanupTimeMs;
 
         public OverflowArrayMap(int uid) {
             mUid = uid;
@@ -2696,7 +2755,7 @@
         }
 
         public void clear() {
-            mLastClearTime = SystemClock.elapsedRealtime();
+            mLastClearTimeMs = SystemClock.elapsedRealtime();
             mMap.clear();
             mCurOverflow = null;
             mActiveOverflow = null;
@@ -2712,8 +2771,8 @@
             }
         }
 
-        public void cleanup() {
-            mLastCleanupTime = SystemClock.elapsedRealtime();
+        public void cleanup(long elapsedRealtimeMs) {
+            mLastCleanupTimeMs = elapsedRealtimeMs;
             if (mActiveOverflow != null) {
                 if (mActiveOverflow.size() == 0) {
                     mActiveOverflow = null;
@@ -2737,7 +2796,7 @@
             }
         }
 
-        public T startObject(String name) {
+        public T startObject(String name, long elapsedRealtimeMs) {
             if (name == null) {
                 name = "";
             }
@@ -2780,7 +2839,7 @@
                     mActiveOverflow = new ArrayMap<>();
                 }
                 mActiveOverflow.put(name, new MutableInt(1));
-                mLastOverflowTime = SystemClock.elapsedRealtime();
+                mLastOverflowTimeMs = elapsedRealtimeMs;
                 return obj;
             }
 
@@ -2790,7 +2849,7 @@
             return obj;
         }
 
-        public T stopObject(String name) {
+        public T stopObject(String name, long elapsedRealtimeMs) {
             if (name == null) {
                 name = "";
             }
@@ -2810,7 +2869,7 @@
                         over.value--;
                         if (over.value <= 0) {
                             mActiveOverflow.remove(name);
-                            mLastOverflowFinishTime = SystemClock.elapsedRealtime();
+                            mLastOverflowFinishTimeMs = elapsedRealtimeMs;
                         }
                         return obj;
                     }
@@ -2830,22 +2889,22 @@
             sb.append(mActiveOverflow);
             sb.append(" curoverflow=");
             sb.append(mCurOverflow);
-            long now = SystemClock.elapsedRealtime();
-            if (mLastOverflowTime != 0) {
+            long now = elapsedRealtimeMs;
+            if (mLastOverflowTimeMs != 0) {
                 sb.append(" lastOverflowTime=");
-                TimeUtils.formatDuration(mLastOverflowTime-now, sb);
+                TimeUtils.formatDuration(mLastOverflowTimeMs - now, sb);
             }
-            if (mLastOverflowFinishTime != 0) {
+            if (mLastOverflowFinishTimeMs != 0) {
                 sb.append(" lastOverflowFinishTime=");
-                TimeUtils.formatDuration(mLastOverflowFinishTime-now, sb);
+                TimeUtils.formatDuration(mLastOverflowFinishTimeMs - now, sb);
             }
-            if (mLastClearTime != 0) {
+            if (mLastClearTimeMs != 0) {
                 sb.append(" lastClearTime=");
-                TimeUtils.formatDuration(mLastClearTime-now, sb);
+                TimeUtils.formatDuration(mLastClearTimeMs - now, sb);
             }
-            if (mLastCleanupTime != 0) {
+            if (mLastCleanupTimeMs != 0) {
                 sb.append(" lastCleanupTime=");
-                TimeUtils.formatDuration(mLastCleanupTime-now, sb);
+                TimeUtils.formatDuration(mLastCleanupTimeMs - now, sb);
             }
             Slog.wtf(TAG, sb.toString());
             return null;
@@ -2943,16 +3002,16 @@
             mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
         }
 
-        public void reset(boolean detachIfReset) {
-            mIdleTimeMillis.reset(detachIfReset);
-            mScanTimeMillis.reset(detachIfReset);
-            mSleepTimeMillis.reset(detachIfReset);
-            mRxTimeMillis.reset(detachIfReset);
+        public void reset(boolean detachIfReset, long elapsedRealtimeUs) {
+            mIdleTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
+            mScanTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
+            mSleepTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
+            mRxTimeMillis.reset(detachIfReset, elapsedRealtimeUs);
             for (LongSamplingCounter counter : mTxTimeMillis) {
-                counter.reset(detachIfReset);
+                counter.reset(detachIfReset, elapsedRealtimeUs);
             }
-            mPowerDrainMaMs.reset(detachIfReset);
-            mMonitoredRailChargeConsumedMaMs.reset(detachIfReset);
+            mPowerDrainMaMs.reset(detachIfReset, elapsedRealtimeUs);
+            mMonitoredRailChargeConsumedMaMs.reset(detachIfReset, elapsedRealtimeUs);
         }
 
         public void detach() {
@@ -3416,82 +3475,82 @@
             final int NU = mUidStats.size();
             for (int i=0; i<NU; i++) {
                 final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-                uid.mLastStepUserTime = uid.mCurStepUserTime;
-                uid.mLastStepSystemTime = uid.mCurStepSystemTime;
+                uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+                uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
             }
-            mLastStepCpuUserTime = mCurStepCpuUserTime;
-            mLastStepCpuSystemTime = mCurStepCpuSystemTime;
-            mLastStepStatUserTime = mCurStepStatUserTime;
-            mLastStepStatSystemTime = mCurStepStatSystemTime;
-            mLastStepStatIOWaitTime = mCurStepStatIOWaitTime;
-            mLastStepStatIrqTime = mCurStepStatIrqTime;
-            mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime;
-            mLastStepStatIdleTime = mCurStepStatIdleTime;
+            mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+            mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+            mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+            mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+            mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+            mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+            mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+            mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
             tmp.clear();
             return;
         }
         if (DEBUG) {
-            Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTime + " sys="
-                    + mLastStepStatSystemTime + " io=" + mLastStepStatIOWaitTime
-                    + " irq=" + mLastStepStatIrqTime + " sirq="
-                    + mLastStepStatSoftIrqTime + " idle=" + mLastStepStatIdleTime);
-            Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTime + " sys="
-                    + mCurStepStatSystemTime + " io=" + mCurStepStatIOWaitTime
-                    + " irq=" + mCurStepStatIrqTime + " sirq="
-                    + mCurStepStatSoftIrqTime + " idle=" + mCurStepStatIdleTime);
+            Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
+                    + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
+                    + " irq=" + mLastStepStatIrqTimeMs + " sirq="
+                    + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
+            Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
+                    + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
+                    + " irq=" + mCurStepStatIrqTimeMs + " sirq="
+                    + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
         }
-        out.userTime = (int)(mCurStepCpuUserTime - mLastStepCpuUserTime);
-        out.systemTime = (int)(mCurStepCpuSystemTime - mLastStepCpuSystemTime);
-        out.statUserTime = (int)(mCurStepStatUserTime - mLastStepStatUserTime);
-        out.statSystemTime = (int)(mCurStepStatSystemTime - mLastStepStatSystemTime);
-        out.statIOWaitTime = (int)(mCurStepStatIOWaitTime - mLastStepStatIOWaitTime);
-        out.statIrqTime = (int)(mCurStepStatIrqTime - mLastStepStatIrqTime);
-        out.statSoftIrqTime = (int)(mCurStepStatSoftIrqTime - mLastStepStatSoftIrqTime);
-        out.statIdlTime = (int)(mCurStepStatIdleTime - mLastStepStatIdleTime);
+        out.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
+        out.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
+        out.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
+        out.statSystemTime = (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
+        out.statIOWaitTime = (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
+        out.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
+        out.statSoftIrqTime = (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
+        out.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
         out.appCpuUid1 = out.appCpuUid2 = out.appCpuUid3 = -1;
         out.appCpuUTime1 = out.appCpuUTime2 = out.appCpuUTime3 = 0;
         out.appCpuSTime1 = out.appCpuSTime2 = out.appCpuSTime3 = 0;
         final int NU = mUidStats.size();
         for (int i=0; i<NU; i++) {
             final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-            final int totalUTime = (int)(uid.mCurStepUserTime - uid.mLastStepUserTime);
-            final int totalSTime = (int)(uid.mCurStepSystemTime - uid.mLastStepSystemTime);
-            final int totalTime = totalUTime + totalSTime;
-            uid.mLastStepUserTime = uid.mCurStepUserTime;
-            uid.mLastStepSystemTime = uid.mCurStepSystemTime;
-            if (totalTime <= (out.appCpuUTime3+out.appCpuSTime3)) {
+            final int totalUTimeMs = (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
+            final int totalSTimeMs = (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
+            final int totalTimeMs = totalUTimeMs + totalSTimeMs;
+            uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+            uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
+            if (totalTimeMs <= (out.appCpuUTime3 + out.appCpuSTime3)) {
                 continue;
             }
-            if (totalTime <= (out.appCpuUTime2+out.appCpuSTime2)) {
+            if (totalTimeMs <= (out.appCpuUTime2 + out.appCpuSTime2)) {
                 out.appCpuUid3 = uid.mUid;
-                out.appCpuUTime3 = totalUTime;
-                out.appCpuSTime3 = totalSTime;
+                out.appCpuUTime3 = totalUTimeMs;
+                out.appCpuSTime3 = totalSTimeMs;
             } else {
                 out.appCpuUid3 = out.appCpuUid2;
                 out.appCpuUTime3 = out.appCpuUTime2;
                 out.appCpuSTime3 = out.appCpuSTime2;
-                if (totalTime <= (out.appCpuUTime1+out.appCpuSTime1)) {
+                if (totalTimeMs <= (out.appCpuUTime1 + out.appCpuSTime1)) {
                     out.appCpuUid2 = uid.mUid;
-                    out.appCpuUTime2 = totalUTime;
-                    out.appCpuSTime2 = totalSTime;
+                    out.appCpuUTime2 = totalUTimeMs;
+                    out.appCpuSTime2 = totalSTimeMs;
                 } else {
                     out.appCpuUid2 = out.appCpuUid1;
                     out.appCpuUTime2 = out.appCpuUTime1;
                     out.appCpuSTime2 = out.appCpuSTime1;
                     out.appCpuUid1 = uid.mUid;
-                    out.appCpuUTime1 = totalUTime;
-                    out.appCpuSTime1 = totalSTime;
+                    out.appCpuUTime1 = totalUTimeMs;
+                    out.appCpuSTime1 = totalSTimeMs;
                 }
             }
         }
-        mLastStepCpuUserTime = mCurStepCpuUserTime;
-        mLastStepCpuSystemTime = mCurStepCpuSystemTime;
-        mLastStepStatUserTime = mCurStepStatUserTime;
-        mLastStepStatSystemTime = mCurStepStatSystemTime;
-        mLastStepStatIOWaitTime = mCurStepStatIOWaitTime;
-        mLastStepStatIrqTime = mCurStepStatIrqTime;
-        mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime;
-        mLastStepStatIdleTime = mCurStepStatIdleTime;
+        mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+        mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+        mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+        mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+        mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+        mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+        mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+        mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
     }
 
     public void readHistoryDelta(Parcel src, HistoryItem cur) {
@@ -3631,29 +3690,35 @@
     }
 
     public void createFakeHistoryEvents(long numEvents) {
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+        final long uptimeMs = mClocks.uptimeMillis();
         for(long i = 0; i < numEvents; i++) {
-            noteLongPartialWakelockStart("name1", "historyName1", 1000);
-            noteLongPartialWakelockFinish("name1", "historyName1", 1000);
+            noteLongPartialWakelockStart("name1", "historyName1", 1000,
+                    elapsedRealtimeMs, uptimeMs);
+            noteLongPartialWakelockFinish("name1", "historyName1", 1000,
+                    elapsedRealtimeMs, uptimeMs);
         }
     }
 
-    void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) {
+    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
 
-        final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
+        final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
         final int diffStates = mHistoryLastWritten.states^(cur.states&mActiveHistoryStates);
         final int diffStates2 = mHistoryLastWritten.states2^(cur.states2&mActiveHistoryStates2);
         final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
         final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
-        if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
-                + Integer.toHexString(diffStates) + " lastDiff="
-                + Integer.toHexString(lastDiffStates) + " diff2="
-                + Integer.toHexString(diffStates2) + " lastDiff2="
-                + Integer.toHexString(lastDiffStates2));
+        if (DEBUG) {
+            Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff="
+                    + Integer.toHexString(diffStates) + " lastDiff="
+                    + Integer.toHexString(lastDiffStates) + " diff2="
+                    + Integer.toHexString(diffStates2) + " lastDiff2="
+                    + Integer.toHexString(lastDiffStates2));
+        }
         if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
-                && timeDiff < 1000 && (diffStates&lastDiffStates) == 0
+                && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
                 && (diffStates2&lastDiffStates2) == 0
                 && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
                 && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
@@ -3674,7 +3739,7 @@
             mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
             mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
             mHistoryBufferLastPos = -1;
-            elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTime;
+            elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
             // If the last written history had a wakelock tag, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
@@ -3714,11 +3779,9 @@
             mHistoryBuffer.setDataPosition(0);
             mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
             mHistoryBufferLastPos = -1;
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             HistoryItem newItem = new HistoryItem();
             newItem.setTo(cur);
-            startRecordingHistory(elapsedRealtime, uptime, false);
+            startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
             addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, newItem);
             return;
         }
@@ -3737,11 +3800,11 @@
         }
         mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
         mHistoryLastLastWritten.setTo(mHistoryLastWritten);
-        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
+        mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
         mHistoryLastWritten.states &= mActiveHistoryStates;
         mHistoryLastWritten.states2 &= mActiveHistoryStates2;
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
-        mLastHistoryElapsedRealtime = elapsedRealtimeMs;
+        mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
         cur.wakelockTag = null;
         cur.wakeReasonTag = null;
         cur.eventCode = HistoryItem.EVENT_NONE;
@@ -3755,27 +3818,27 @@
     int mChangedStates2 = 0;
 
     void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
-        if (mTrackRunningHistoryElapsedRealtime != 0) {
-            final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
-            final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
-            if (diffUptime < (diffElapsed-20)) {
-                final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
+        if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
+            final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
+            final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs;
+            if (diffUptimeMs < (diffElapsedMs - 20)) {
+                final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs);
                 mHistoryAddTmp.setTo(mHistoryLastWritten);
                 mHistoryAddTmp.wakelockTag = null;
                 mHistoryAddTmp.wakeReasonTag = null;
                 mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
                 mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
-                addHistoryRecordInnerLocked(wakeElapsedTime, mHistoryAddTmp);
+                addHistoryRecordInnerLocked(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
             }
         }
         mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
-        mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
-        mTrackRunningHistoryUptime = uptimeMs;
-        addHistoryRecordInnerLocked(elapsedRealtimeMs, mHistoryCur);
+        mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs;
+        mTrackRunningHistoryUptimeMs = uptimeMs;
+        addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
     }
 
-    void addHistoryRecordInnerLocked(long elapsedRealtimeMs, HistoryItem cur) {
-        addHistoryBufferLocked(elapsedRealtimeMs, cur);
+    void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
 
         if (!USE_OLD_HISTORY) {
             return;
@@ -3790,13 +3853,13 @@
         // are now resetting back to their original value, then just collapse
         // into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
-                && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
+                && (mHistoryBaseTimeMs + elapsedRealtimeMs) < (mHistoryEnd.time + 1000)
                 && ((mHistoryEnd.states^cur.states)&mChangedStates&mActiveHistoryStates) == 0
                 && ((mHistoryEnd.states2^cur.states2)&mChangedStates2&mActiveHistoryStates2) == 0) {
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
-                    && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
+                    && (mHistoryBaseTimeMs + elapsedRealtimeMs) < (mHistoryEnd.time + 500)
                     && mHistoryLastEnd.sameNonEvent(cur)) {
                 mHistoryLastEnd.next = null;
                 mHistoryEnd.next = mHistoryCache;
@@ -3832,7 +3895,7 @@
         } else {
             rec = new HistoryItem();
         }
-        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
+        rec.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
 
         addHistoryRecordLocked(rec);
     }
@@ -3860,10 +3923,10 @@
             mNumHistoryItems = 0;
         }
 
-        mHistoryBaseTime = 0;
-        mLastHistoryElapsedRealtime = 0;
-        mTrackRunningHistoryElapsedRealtime = 0;
-        mTrackRunningHistoryUptime = 0;
+        mHistoryBaseTimeMs = 0;
+        mLastHistoryElapsedRealtimeMs = 0;
+        mTrackRunningHistoryElapsedRealtimeMs = 0;
+        mTrackRunningHistoryUptimeMs = 0;
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
@@ -3879,8 +3942,8 @@
     }
 
     @GuardedBy("this")
-    public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
-            long realtime) {
+    public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptimeUs,
+            long realtimeUs) {
         final boolean screenOff = !isScreenOn(screenState);
         final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
         final boolean updateOnBatteryScreenOffTimeBase =
@@ -3888,14 +3951,15 @@
 
         if (updateOnBatteryScreenOffTimeBase || updateOnBatteryTimeBase) {
             if (updateOnBatteryScreenOffTimeBase) {
-                updateKernelWakelocksLocked();
+                updateKernelWakelocksLocked(realtimeUs);
                 updateBatteryPropertiesLocked();
             }
             // This if{} is only necessary due to SCREEN_OFF_RPM_STATS_ENABLED, which exists because
             // updateRpmStatsLocked is too slow to run each screen change. When the speed is
             // improved, remove the surrounding if{}.
             if (SCREEN_OFF_RPM_STATS_ENABLED || updateOnBatteryTimeBase) {
-                updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
+                // if either OnBattery or OnBatteryScreenOfftimebase changes.
+                updateRpmStatsLocked(realtimeUs);
             }
             if (DEBUG_ENERGY_CPU) {
                 Slog.d(TAG, "Updating cpu time because screen is now "
@@ -3903,16 +3967,17 @@
                         + " and battery is " + (unplugged ? "on" : "off"));
             }
 
-            mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
+            mOnBatteryTimeBase.setRunning(unplugged, uptimeUs, realtimeUs);
             if (updateOnBatteryTimeBase) {
                 for (int i = mUidStats.size() - 1; i >= 0; --i) {
-                    mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptime, realtime);
+                    mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptimeUs, realtimeUs);
                 }
             }
             if (updateOnBatteryScreenOffTimeBase) {
-                mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
+                mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff,
+                        uptimeUs, realtimeUs);
                 for (int i = mUidStats.size() - 1; i >= 0; --i) {
-                    mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
+                    mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptimeUs, realtimeUs);
                 }
             }
         }
@@ -3931,8 +3996,14 @@
     }
 
     public void addIsolatedUidLocked(int isolatedUid, int appUid) {
+        addIsolatedUidLocked(isolatedUid, appUid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void addIsolatedUidLocked(int isolatedUid, int appUid,
+            long elapsedRealtimeMs, long uptimeMs) {
         mIsolatedUids.put(isolatedUid, appUid);
-        final Uid u = getUidStatsLocked(appUid);
+        final Uid u = getUidStatsLocked(appUid, elapsedRealtimeMs, uptimeMs);
         u.addIsolatedUid(isolatedUid);
     }
 
@@ -3955,14 +4026,22 @@
      */
     @GuardedBy("this")
     public void removeIsolatedUidLocked(int isolatedUid) {
+        removeIsolatedUidLocked(isolatedUid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    /**
+     * @see #removeIsolatedUidLocked(int)
+     */
+    @GuardedBy("this")
+    public void removeIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs, long uptimeMs) {
         final int idx = mIsolatedUids.indexOfKey(isolatedUid);
         if (idx >= 0) {
             final int ownerUid = mIsolatedUids.valueAt(idx);
-            final Uid u = getUidStatsLocked(ownerUid);
+            final Uid u = getUidStatsLocked(ownerUid, elapsedRealtimeMs, uptimeMs);
             u.removeIsolatedUid(isolatedUid);
             mIsolatedUids.removeAt(idx);
         }
-        mPendingRemovedUids.add(new UidToRemove(isolatedUid, mClocks.elapsedRealtime()));
+        mPendingRemovedUids.add(new UidToRemove(isolatedUid, elapsedRealtimeMs));
     }
 
     public int mapUid(int uid) {
@@ -3971,26 +4050,39 @@
     }
 
     public void noteEventLocked(int code, String name, int uid) {
+        noteEventLocked(code, name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteEventLocked(int code, String name, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (!mActiveEvents.updateState(code, name, uid, 0)) {
             return;
         }
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, code, name, uid);
     }
 
     public void noteCurrentTimeChangedLocked() {
         final long currentTime = System.currentTimeMillis();
         final long elapsedRealtime = mClocks.elapsedRealtime();
         final long uptime = mClocks.uptimeMillis();
-        recordCurrentTimeChangeLocked(currentTime, elapsedRealtime, uptime);
+        noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime);
+    }
+
+    public void noteCurrentTimeChangedLocked(long currentTimeMs,
+            long elapsedRealtimeMs, long uptimeMs) {
+        recordCurrentTimeChangeLocked(currentTimeMs, elapsedRealtimeMs, uptimeMs);
     }
 
     public void noteProcessStartLocked(String name, int uid) {
+        noteProcessStartLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteProcessStartLocked(String name, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (isOnBattery()) {
-            Uid u = getUidStatsLocked(uid);
+            Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
             u.getProcessStatsLocked(name).incStartsLocked();
         }
         if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_START, name, uid, 0)) {
@@ -3999,28 +4091,40 @@
         if (!mRecordAllHistory) {
             return;
         }
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_START, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
     }
 
     public void noteProcessCrashLocked(String name, int uid) {
+        noteProcessCrashLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteProcessCrashLocked(String name, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (isOnBattery()) {
-            Uid u = getUidStatsLocked(uid);
+            Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
             u.getProcessStatsLocked(name).incNumCrashesLocked();
         }
     }
 
     public void noteProcessAnrLocked(String name, int uid) {
+        noteProcessAnrLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteProcessAnrLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (isOnBattery()) {
-            Uid u = getUidStatsLocked(uid);
+            Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
             u.getProcessStatsLocked(name).incNumAnrsLocked();
         }
     }
 
     public void noteUidProcessStateLocked(int uid, int state) {
+        noteUidProcessStateLocked(uid, state, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteUidProcessStateLocked(int uid, int state,
+            long elapsedRealtimeMs, long uptimeMs) {
         int parentUid = mapUid(uid);
         if (uid != parentUid) {
             // Isolated UIDs process state is already rolled up into parent, so no need to track
@@ -4032,10 +4136,16 @@
         // and isolated uids rather than only the parent uid.
         FrameworkStatsLog.write(FrameworkStatsLog.UID_PROCESS_STATE_CHANGED, uid,
                 ActivityManager.processStateAmToProto(state));
-        getUidStatsLocked(uid).updateUidProcessStateLocked(state);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .updateUidProcessStateLocked(state, elapsedRealtimeMs, uptimeMs);
     }
 
     public void noteProcessFinishLocked(String name, int uid) {
+        noteProcessFinishLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteProcessFinishLocked(String name, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_FINISH, name, uid, 0)) {
             return;
@@ -4043,82 +4153,120 @@
         if (!mRecordAllHistory) {
             return;
         }
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_FINISH, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_FINISH,
+                name, uid);
     }
 
     public void noteSyncStartLocked(String name, int uid) {
+        noteSyncStartLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteSyncStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        getUidStatsLocked(uid).noteStartSyncLocked(name, elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteStartSyncLocked(name, elapsedRealtimeMs);
         if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_START, name, uid, 0)) {
             return;
         }
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SYNC_START, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
     }
 
     public void noteSyncFinishLocked(String name, int uid) {
+        noteSyncFinishLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteSyncFinishLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        getUidStatsLocked(uid).noteStopSyncLocked(name, elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteStopSyncLocked(name, elapsedRealtimeMs);
         if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_FINISH, name, uid, 0)) {
             return;
         }
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SYNC_FINISH, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_FINISH,
+                name, uid);
     }
 
     public void noteJobStartLocked(String name, int uid) {
+        noteJobStartLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteJobStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        getUidStatsLocked(uid).noteStartJobLocked(name, elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteStartJobLocked(name, elapsedRealtimeMs);
         if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_START, name, uid, 0)) {
             return;
         }
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_START, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
     }
 
     public void noteJobFinishLocked(String name, int uid, int stopReason) {
+        noteJobFinishLocked(name, uid, stopReason,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteJobFinishLocked(String name, int uid, int stopReason,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        getUidStatsLocked(uid).noteStopJobLocked(name, elapsedRealtime, stopReason);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteStopJobLocked(name, elapsedRealtimeMs, stopReason);
         if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_FINISH, name, uid, 0)) {
             return;
         }
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_FINISH, name, uid);
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
     }
 
     public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast) {
+        noteJobsDeferredLocked(uid, numDeferred, sinceLast,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        getUidStatsLocked(uid).noteJobsDeferredLocked(numDeferred, sinceLast);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteJobsDeferredLocked(numDeferred, sinceLast);
     }
 
     public void noteAlarmStartLocked(String name, WorkSource workSource, int uid) {
-        noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_START, name, workSource, uid);
+        noteAlarmStartLocked(name, workSource, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteAlarmStartLocked(String name, WorkSource workSource, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
+        noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_START, name, workSource, uid,
+                elapsedRealtimeMs, uptimeMs);
     }
 
     public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid) {
-        noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_FINISH, name, workSource, uid);
+        noteAlarmFinishLocked(name, workSource, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
+        noteAlarmStartOrFinishLocked(HistoryItem.EVENT_ALARM_FINISH, name, workSource, uid,
+                elapsedRealtimeMs, uptimeMs);
     }
 
     private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
             int uid) {
+        noteAlarmStartOrFinishLocked(historyItem, name, workSource, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
+            int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (!mRecordAllHistory) {
             return;
         }
 
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-
         if (workSource != null) {
             for (int i = 0; i < workSource.size(); ++i) {
                 uid = mapUid(workSource.getUid(i));
                 if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
-                    addHistoryEventLocked(elapsedRealtime, uptime, historyItem, name, uid);
+                    addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
                 }
             }
 
@@ -4127,7 +4275,7 @@
                 for (int i = 0; i < workChains.size(); ++i) {
                     uid = mapUid(workChains.get(i).getAttributionUid());
                     if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
-                        addHistoryEventLocked(elapsedRealtime, uptime, historyItem, name, uid);
+                        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
                     }
                 }
             }
@@ -4135,13 +4283,19 @@
             uid = mapUid(uid);
 
             if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
-                addHistoryEventLocked(elapsedRealtime, uptime, historyItem, name, uid);
+                addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
             }
         }
     }
 
     public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
             String tag) {
+        noteWakupAlarmLocked(packageName, uid, workSource, tag,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
+            String tag, long elapsedRealtimeMs, long uptimeMs) {
         if (workSource != null) {
             for (int i = 0; i < workSource.size(); ++i) {
                 uid = workSource.getUid(i);
@@ -4149,7 +4303,8 @@
 
                 if (isOnBattery()) {
                     BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid,
-                            workSourceName != null ? workSourceName : packageName);
+                            workSourceName != null ? workSourceName : packageName,
+                            elapsedRealtimeMs, uptimeMs);
                     pkg.noteWakeupAlarmLocked(tag);
                 }
             }
@@ -4161,14 +4316,16 @@
                     uid = wc.getAttributionUid();
 
                     if (isOnBattery()) {
-                        BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName);
+                        BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName,
+                                elapsedRealtimeMs, uptimeMs);
                         pkg.noteWakeupAlarmLocked(tag);
                     }
                 }
             }
         } else {
             if (isOnBattery()) {
-                BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName);
+                BatteryStatsImpl.Uid.Pkg pkg = getPackageStatsLocked(uid, packageName,
+                        elapsedRealtimeMs, uptimeMs);
                 pkg.noteWakeupAlarmLocked(tag);
             }
         }
@@ -4228,7 +4385,8 @@
     public void setPretendScreenOff(boolean pretendScreenOff) {
         if (mPretendScreenOff != pretendScreenOff) {
             mPretendScreenOff = pretendScreenOff;
-            noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+            noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON,
+                    mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis());
         }
     }
 
@@ -4236,19 +4394,25 @@
     private int mInitialAcquireWakeUid = -1;
 
     public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
-        int type, boolean unimportantForLogging, long elapsedRealtime, long uptime) {
+            int type, boolean unimportantForLogging) {
+        noteStartWakeLocked(uid, pid, wc, name, historyName, type, unimportantForLogging,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
+            int type, boolean unimportantForLogging, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
-            aggregateLastWakeupUptimeLocked(uptime);
+            aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
             if (historyName == null) {
                 historyName = name;
             }
             if (mRecordAllHistory) {
                 if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
                         uid, 0)) {
-                    addHistoryEventLocked(elapsedRealtime, uptime,
+                    addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
                             HistoryItem.EVENT_WAKE_LOCK_START, historyName, uid);
                 }
             }
@@ -4260,7 +4424,7 @@
                 mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
                 mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                 mWakeLockImportant = !unimportantForLogging;
-                addHistoryRecordLocked(elapsedRealtime, uptime);
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             } else if (!mWakeLockImportant && !unimportantForLogging
                     && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE) {
                 if (mHistoryLastWritten.wakelockTag != null) {
@@ -4269,7 +4433,7 @@
                     mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
                     mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName;
                     mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
-                    addHistoryRecordLocked(elapsedRealtime, uptime);
+                    addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
                 }
                 mWakeLockImportant = true;
             }
@@ -4285,7 +4449,8 @@
                 requestWakelockCpuUpdate();
             }
 
-            getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
 
             if (wc != null) {
                 FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
@@ -4300,7 +4465,13 @@
     }
 
     public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
-            int type, long elapsedRealtime, long uptime) {
+            int type) {
+        noteStopWakeLocked(uid, pid, wc, name, historyName, type,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
+            int type, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             mWakeLockNesting--;
@@ -4310,7 +4481,7 @@
                 }
                 if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
                         uid, 0)) {
-                    addHistoryEventLocked(elapsedRealtime, uptime,
+                    addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
                             HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, uid);
                 }
             }
@@ -4320,7 +4491,7 @@
                         + Integer.toHexString(mHistoryCur.states));
                 mInitialAcquireWakeName = null;
                 mInitialAcquireWakeUid = -1;
-                addHistoryRecordLocked(elapsedRealtime, uptime);
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             }
         }
         if (uid >= 0) {
@@ -4331,7 +4502,8 @@
                 requestWakelockCpuUpdate();
             }
 
-            getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
             if (wc != null) {
                 FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
                         wc.getTags(), getPowerManagerWakeLockLevel(type), name,
@@ -4377,12 +4549,17 @@
 
     public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
             String historyName, int type, boolean unimportantForLogging) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteStartWakeFromSourceLocked(ws, pid, name, historyName, type, unimportantForLogging,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
+            String historyName, int type, boolean unimportantForLogging,
+            long elapsedRealtimeMs, long uptimeMs) {
         final int N = ws.size();
         for (int i=0; i<N; i++) {
             noteStartWakeLocked(ws.getUid(i), pid, null, name, historyName, type,
-                    unimportantForLogging, elapsedRealtime, uptime);
+                    unimportantForLogging, elapsedRealtimeMs, uptimeMs);
         }
 
         List<WorkChain> wcs = ws.getWorkChains();
@@ -4390,7 +4567,7 @@
             for (int i = 0; i < wcs.size(); ++i) {
                 final WorkChain wc = wcs.get(i);
                 noteStartWakeLocked(wc.getAttributionUid(), pid, wc, name, historyName, type,
-                        unimportantForLogging, elapsedRealtime, uptime);
+                        unimportantForLogging, elapsedRealtimeMs, uptimeMs);
             }
         }
     }
@@ -4398,9 +4575,15 @@
     public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
             String historyName, int type, WorkSource newWs, int newPid, String newName,
             String newHistoryName, int newType, boolean newUnimportantForLogging) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type, newWs, newPid,
+                newName, newHistoryName, newType, newUnimportantForLogging,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
 
+    public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
+            String historyName, int type, WorkSource newWs, int newPid, String newName,
+            String newHistoryName, int newType, boolean newUnimportantForLogging,
+            long elapsedRealtimeMs, long uptimeMs) {
         List<WorkChain>[] wcs = WorkSource.diffChains(ws, newWs);
 
         // For correct semantics, we start the need worksources first, so that we won't
@@ -4411,7 +4594,7 @@
         final int NN = newWs.size();
         for (int i=0; i<NN; i++) {
             noteStartWakeLocked(newWs.getUid(i), newPid, null, newName, newHistoryName, newType,
-                    newUnimportantForLogging, elapsedRealtime, uptime);
+                    newUnimportantForLogging, elapsedRealtimeMs, uptimeMs);
         }
         if (wcs != null) {
             List<WorkChain> newChains = wcs[0];
@@ -4419,8 +4602,8 @@
                 for (int i = 0; i < newChains.size(); ++i) {
                     final WorkChain newChain = newChains.get(i);
                     noteStartWakeLocked(newChain.getAttributionUid(), newPid, newChain, newName,
-                        newHistoryName, newType, newUnimportantForLogging, elapsedRealtime,
-                        uptime);
+                            newHistoryName, newType, newUnimportantForLogging, elapsedRealtimeMs,
+                            uptimeMs);
                 }
             }
         }
@@ -4428,8 +4611,8 @@
         // Then the stops :
         final int NO = ws.size();
         for (int i=0; i<NO; i++) {
-            noteStopWakeLocked(ws.getUid(i), pid, null, name, historyName, type, elapsedRealtime,
-                    uptime);
+            noteStopWakeLocked(ws.getUid(i), pid, null, name, historyName, type, elapsedRealtimeMs,
+                    uptimeMs);
         }
         if (wcs != null) {
             List<WorkChain> goneChains = wcs[1];
@@ -4437,7 +4620,7 @@
                 for (int i = 0; i < goneChains.size(); ++i) {
                     final WorkChain goneChain = goneChains.get(i);
                     noteStopWakeLocked(goneChain.getAttributionUid(), pid, goneChain, name,
-                            historyName, type, elapsedRealtime, uptime);
+                            historyName, type, elapsedRealtimeMs, uptimeMs);
                 }
             }
         }
@@ -4445,12 +4628,16 @@
 
     public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
             String historyName, int type) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteStopWakeFromSourceLocked(ws, pid, name, historyName, type,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
+            String historyName, int type, long elapsedRealtimeMs, long uptimeMs) {
         final int N = ws.size();
         for (int i=0; i<N; i++) {
-            noteStopWakeLocked(ws.getUid(i), pid, null, name, historyName, type, elapsedRealtime,
-                    uptime);
+            noteStopWakeLocked(ws.getUid(i), pid, null, name, historyName, type, elapsedRealtimeMs,
+                    uptimeMs);
         }
 
         List<WorkChain> wcs = ws.getWorkChains();
@@ -4458,22 +4645,35 @@
             for (int i = 0; i < wcs.size(); ++i) {
                 final WorkChain wc = wcs.get(i);
                 noteStopWakeLocked(wc.getAttributionUid(), pid, wc, name, historyName, type,
-                        elapsedRealtime, uptime);
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
+        noteLongPartialWakelockStart(name, historyName, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteLongPartialWakelockStart(String name, String historyName, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        noteLongPartialWakeLockStartInternal(name, historyName, uid);
+        noteLongPartialWakeLockStartInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
     }
 
     public void noteLongPartialWakelockStartFromSource(String name, String historyName,
             WorkSource workSource) {
+        noteLongPartialWakelockStartFromSource(name, historyName, workSource,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteLongPartialWakelockStartFromSource(String name, String historyName,
+            WorkSource workSource, long elapsedRealtimeMs, long uptimeMs) {
         final int N = workSource.size();
         for (int i = 0; i < N; ++i) {
             final int uid = mapUid(workSource.getUid(i));
-            noteLongPartialWakeLockStartInternal(name, historyName, uid);
+            noteLongPartialWakeLockStartInternal(name, historyName, uid,
+                    elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = workSource.getWorkChains();
@@ -4481,14 +4681,14 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain workChain = workChains.get(i);
                 final int uid = workChain.getAttributionUid();
-                noteLongPartialWakeLockStartInternal(name, historyName, uid);
+                noteLongPartialWakeLockStartInternal(name, historyName, uid,
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
-    private void noteLongPartialWakeLockStartInternal(String name, String historyName, int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+    private void noteLongPartialWakeLockStartInternal(String name, String historyName, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (historyName == null) {
             historyName = name;
         }
@@ -4496,21 +4696,34 @@
                 0)) {
             return;
         }
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
                 historyName, uid);
     }
 
     public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
+        noteLongPartialWakelockFinish(name, historyName, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteLongPartialWakelockFinish(String name, String historyName, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        noteLongPartialWakeLockFinishInternal(name, historyName, uid);
+        noteLongPartialWakeLockFinishInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
     }
 
     public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
             WorkSource workSource) {
+        noteLongPartialWakelockFinishFromSource(name, historyName, workSource,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
+            WorkSource workSource, long elapsedRealtimeMs, long uptimeMs) {
         final int N = workSource.size();
         for (int i = 0; i < N; ++i) {
             final int uid = mapUid(workSource.getUid(i));
-            noteLongPartialWakeLockFinishInternal(name, historyName, uid);
+            noteLongPartialWakeLockFinishInternal(name, historyName, uid,
+                    elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = workSource.getWorkChains();
@@ -4518,14 +4731,14 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain workChain = workChains.get(i);
                 final int uid = workChain.getAttributionUid();
-                noteLongPartialWakeLockFinishInternal(name, historyName, uid);
+                noteLongPartialWakeLockFinishInternal(name, historyName, uid,
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
-    private void noteLongPartialWakeLockFinishInternal(String name, String historyName, int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+    private void noteLongPartialWakeLockFinishInternal(String name, String historyName, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (historyName == null) {
             historyName = name;
         }
@@ -4533,33 +4746,35 @@
                 0)) {
             return;
         }
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
                 historyName, uid);
     }
 
-    void aggregateLastWakeupUptimeLocked(long uptimeMs) {
+    void aggregateLastWakeupUptimeLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mLastWakeupReason != null) {
-            long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
+            long deltaUptimeMs = uptimeMs - mLastWakeupUptimeMs;
             SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
-            timer.add(deltaUptime * 1000, 1); // time in in microseconds
+            timer.add(deltaUptimeMs * 1000, 1, elapsedRealtimeMs); // time in in microseconds
             FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
-                    /* duration_usec */ deltaUptime * 1000);
+                    /* duration_usec */ deltaUptimeMs * 1000);
             mLastWakeupReason = null;
         }
     }
 
     public void noteWakeupReasonLocked(String reason) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteWakeupReasonLocked(reason, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
                 + Integer.toHexString(mHistoryCur.states));
-        aggregateLastWakeupUptimeLocked(uptime);
+        aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
         mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
         mHistoryCur.wakeReasonTag.string = reason;
         mHistoryCur.wakeReasonTag.uid = 0;
         mLastWakeupReason = reason;
-        mLastWakeupUptimeMs = uptime;
-        addHistoryRecordLocked(elapsedRealtime, uptime);
+        mLastWakeupUptimeMs = uptimeMs;
+        addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
     }
 
     public boolean startAddingCpuLocked() {
@@ -4567,21 +4782,23 @@
         return mOnBatteryInternal;
     }
 
-    public void finishAddingCpuLocked(int totalUTime, int totalSTime, int statUserTime,
-                                      int statSystemTime, int statIOWaitTime, int statIrqTime,
-                                      int statSoftIrqTime, int statIdleTime) {
-        if (DEBUG) Slog.d(TAG, "Adding cpu: tuser=" + totalUTime + " tsys=" + totalSTime
-                + " user=" + statUserTime + " sys=" + statSystemTime
-                + " io=" + statIOWaitTime + " irq=" + statIrqTime
-                + " sirq=" + statSoftIrqTime + " idle=" + statIdleTime);
-        mCurStepCpuUserTime += totalUTime;
-        mCurStepCpuSystemTime += totalSTime;
-        mCurStepStatUserTime += statUserTime;
-        mCurStepStatSystemTime += statSystemTime;
-        mCurStepStatIOWaitTime += statIOWaitTime;
-        mCurStepStatIrqTime += statIrqTime;
-        mCurStepStatSoftIrqTime += statSoftIrqTime;
-        mCurStepStatIdleTime += statIdleTime;
+    public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
+                                      int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
+                                      int statSoftIrqTimeMs, int statIdleTimeMs) {
+        if (DEBUG) {
+            Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
+                    + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
+                    + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
+                    + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
+        }
+        mCurStepCpuUserTimeMs += totalUTimeMs;
+        mCurStepCpuSystemTimeMs += totalSTimeMs;
+        mCurStepStatUserTimeMs += statUserTimeMs;
+        mCurStepStatSystemTimeMs += statSystemTimeMs;
+        mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
+        mCurStepStatIrqTimeMs += statIrqTimeMs;
+        mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
+        mCurStepStatIdleTimeMs += statIdleTimeMs;
     }
 
     public void noteProcessDiedLocked(int uid, int pid) {
@@ -4592,65 +4809,76 @@
         }
     }
 
-    public long getProcessWakeTime(int uid, int pid, long realtime) {
+    public long getProcessWakeTime(int uid, int pid, long realtimeMs) {
         uid = mapUid(uid);
         Uid u = mUidStats.get(uid);
         if (u != null) {
             Uid.Pid p = u.mPids.get(pid);
             if (p != null) {
-                return p.mWakeSumMs + (p.mWakeNesting > 0 ? (realtime - p.mWakeStartMs) : 0);
+                return p.mWakeSumMs + (p.mWakeNesting > 0 ? (realtimeMs - p.mWakeStartMs) : 0);
             }
         }
         return 0;
     }
 
-    public void reportExcessiveCpuLocked(int uid, String proc, long overTime, long usedTime) {
+    public void reportExcessiveCpuLocked(int uid, String proc, long overTimeMs, long usedTimeMs) {
         uid = mapUid(uid);
         Uid u = mUidStats.get(uid);
         if (u != null) {
-            u.reportExcessiveCpuLocked(proc, overTime, usedTime);
+            u.reportExcessiveCpuLocked(proc, overTimeMs, usedTimeMs);
         }
     }
 
     int mSensorNesting;
 
     public void noteStartSensorLocked(int uid, int sensor) {
+        noteStartSensorLocked(uid, sensor, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mSensorNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
         mSensorNesting++;
-        getUidStatsLocked(uid).noteStartSensor(sensor, elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteStartSensor(sensor, elapsedRealtimeMs);
     }
 
     public void noteStopSensorLocked(int uid, int sensor) {
+        noteStopSensorLocked(uid, sensor, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteStopSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         mSensorNesting--;
         if (mSensorNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
-        getUidStatsLocked(uid).noteStopSensor(sensor, elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteStopSensor(sensor, elapsedRealtimeMs);
     }
 
     int mGpsNesting;
 
     public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs) {
+        noteGpsChangedLocked(oldWs, newWs, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs,
+            long elapsedRealtimeMs, long uptimeMs) {
         for (int i = 0; i < newWs.size(); ++i) {
-            noteStartGpsLocked(newWs.getUid(i), null);
+            noteStartGpsLocked(newWs.getUid(i), null, elapsedRealtimeMs, uptimeMs);
         }
 
         for (int i = 0; i < oldWs.size(); ++i) {
-            noteStopGpsLocked((oldWs.getUid(i)), null);
+            noteStopGpsLocked((oldWs.getUid(i)), null, elapsedRealtimeMs, uptimeMs);
         }
 
         List<WorkChain>[] wcs = WorkSource.diffChains(oldWs, newWs);
@@ -4658,28 +4886,27 @@
             if (wcs[0] != null) {
                 final List<WorkChain> newChains = wcs[0];
                 for (int i = 0; i < newChains.size(); ++i) {
-                    noteStartGpsLocked(-1, newChains.get(i));
+                    noteStartGpsLocked(-1, newChains.get(i), elapsedRealtimeMs, uptimeMs);
                 }
             }
 
             if (wcs[1] != null) {
                 final List<WorkChain> goneChains = wcs[1];
                 for (int i = 0; i < goneChains.size(); ++i) {
-                    noteStopGpsLocked(-1, goneChains.get(i));
+                    noteStopGpsLocked(-1, goneChains.get(i), elapsedRealtimeMs, uptimeMs);
                 }
             }
         }
     }
 
-    private void noteStartGpsLocked(int uid, WorkChain workChain) {
+    private void noteStartGpsLocked(int uid, WorkChain workChain,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = getAttributionUid(uid, workChain);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mGpsNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
         mGpsNesting++;
 
@@ -4692,20 +4919,19 @@
                     FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
         }
 
-        getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs);
     }
 
-    private void noteStopGpsLocked(int uid, WorkChain workChain) {
+    private void noteStopGpsLocked(int uid, WorkChain workChain,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = getAttributionUid(uid, workChain);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         mGpsNesting--;
         if (mGpsNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            stopAllGpsSignalQualityTimersLocked(-1);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
             mGpsSignalQualityBin = -1;
         }
 
@@ -4717,29 +4943,31 @@
                     workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
         }
 
-        getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs);
     }
 
     public void noteGpsSignalQualityLocked(int signalLevel) {
+        noteGpsSignalQualityLocked(signalLevel, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteGpsSignalQualityLocked(int signalLevel, long elapsedRealtimeMs, long uptimeMs) {
         if (mGpsNesting == 0) {
             return;
         }
         if (signalLevel < 0 || signalLevel >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
-            stopAllGpsSignalQualityTimersLocked(-1);
+            stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
             return;
         }
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mGpsSignalQualityBin != signalLevel) {
             if (mGpsSignalQualityBin >= 0) {
-                mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtime);
+                mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtimeMs);
             }
             if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
-                mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtime);
+                mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtimeMs);
             }
             mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
                     | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mGpsSignalQualityBin = signalLevel;
         }
         return;
@@ -4747,6 +4975,13 @@
 
     @GuardedBy("this")
     public void noteScreenStateLocked(int state) {
+        noteScreenStateLocked(state, mClocks.elapsedRealtime(), mClocks.uptimeMillis(),
+                System.currentTimeMillis());
+    }
+
+    @GuardedBy("this")
+    public void noteScreenStateLocked(int state,
+            long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) {
         state = mPretendScreenOff ? Display.STATE_OFF : state;
 
         // Battery stats relies on there being 4 states. To accommodate this, new states beyond the
@@ -4763,7 +4998,7 @@
         }
 
         if (mScreenState != state) {
-            recordDailyStatsIfNeededLocked(true);
+            recordDailyStatsIfNeededLocked(true, currentTimeMs);
             final int oldState = mScreenState;
             mScreenState = state;
             if (DEBUG) Slog.v(TAG, "Screen state: oldState=" + Display.stateToString(oldState)
@@ -4779,56 +5014,55 @@
                 }
             }
 
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
-
             boolean updateHistory = false;
             if (isScreenDoze(state) && !isScreenDoze(oldState)) {
                 mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
-                mScreenDozeTimer.startRunningLocked(elapsedRealtime);
+                mScreenDozeTimer.startRunningLocked(elapsedRealtimeMs);
                 updateHistory = true;
             } else if (isScreenDoze(oldState) && !isScreenDoze(state)) {
                 mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
-                mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
+                mScreenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
                 updateHistory = true;
             }
             if (isScreenOn(state)) {
                 mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
                         + Integer.toHexString(mHistoryCur.states));
-                mScreenOnTimer.startRunningLocked(elapsedRealtime);
+                mScreenOnTimer.startRunningLocked(elapsedRealtimeMs);
                 if (mScreenBrightnessBin >= 0) {
-                    mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime);
+                    mScreenBrightnessTimer[mScreenBrightnessBin]
+                            .startRunningLocked(elapsedRealtimeMs);
                 }
                 updateHistory = true;
             } else if (isScreenOn(oldState)) {
                 mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
                         + Integer.toHexString(mHistoryCur.states));
-                mScreenOnTimer.stopRunningLocked(elapsedRealtime);
+                mScreenOnTimer.stopRunningLocked(elapsedRealtimeMs);
                 if (mScreenBrightnessBin >= 0) {
-                    mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
+                    mScreenBrightnessTimer[mScreenBrightnessBin]
+                            .stopRunningLocked(elapsedRealtimeMs);
                 }
                 updateHistory = true;
             }
             if (updateHistory) {
                 if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
                         + Display.stateToString(state));
-                addHistoryRecordLocked(elapsedRealtime, uptime);
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             }
             mExternalSync.scheduleCpuSyncDueToScreenStateChange(
                     mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning());
             if (isScreenOn(state)) {
                 updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
-                        mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
+                        uptimeMs * 1000, elapsedRealtimeMs * 1000);
                 // Fake a wake lock, so we consider the device waked as long as the screen is on.
                 noteStartWakeLocked(-1, -1, null, "screen", null, WAKE_TYPE_PARTIAL, false,
-                        elapsedRealtime, uptime);
+                        elapsedRealtimeMs, uptimeMs);
             } else if (isScreenOn(oldState)) {
                 noteStopWakeLocked(-1, -1, null, "screen", "screen", WAKE_TYPE_PARTIAL,
-                        elapsedRealtime, uptime);
+                        elapsedRealtimeMs, uptimeMs);
                 updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
-                        mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
+                        uptimeMs * 1000, elapsedRealtimeMs * 1000);
             }
             // Update discharge amounts.
             if (mOnBatteryInternal) {
@@ -4839,23 +5073,27 @@
 
     @UnsupportedAppUsage
     public void noteScreenBrightnessLocked(int brightness) {
+        noteScreenBrightnessLocked(brightness, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteScreenBrightnessLocked(int brightness, long elapsedRealtimeMs, long uptimeMs) {
         // Bin the brightness.
         int bin = brightness / (256/NUM_SCREEN_BRIGHTNESS_BINS);
         if (bin < 0) bin = 0;
         else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
         if (mScreenBrightnessBin != bin) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
                     | (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             if (mScreenState == Display.STATE_ON) {
                 if (mScreenBrightnessBin >= 0) {
-                    mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
+                    mScreenBrightnessTimer[mScreenBrightnessBin]
+                            .stopRunningLocked(elapsedRealtimeMs);
                 }
-                mScreenBrightnessTimer[bin].startRunningLocked(elapsedRealtime);
+                mScreenBrightnessTimer[bin]
+                        .startRunningLocked(elapsedRealtimeMs);
             }
             mScreenBrightnessBin = bin;
         }
@@ -4863,36 +5101,50 @@
 
     @UnsupportedAppUsage
     public void noteUserActivityLocked(int uid, int event) {
+        noteUserActivityLocked(uid, event, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteUserActivityLocked(int uid, int event, long elapsedRealtimeMs, long uptimeMs) {
         if (mOnBatteryInternal) {
             uid = mapUid(uid);
-            getUidStatsLocked(uid).noteUserActivityLocked(event);
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteUserActivityLocked(event);
         }
     }
 
     public void noteWakeUpLocked(String reason, int reasonUid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SCREEN_WAKE_UP,
+        noteWakeUpLocked(reason, reasonUid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWakeUpLocked(String reason, int reasonUid,
+            long elapsedRealtimeMs, long uptimeMs) {
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP,
                 reason, reasonUid);
     }
 
     public void noteInteractiveLocked(boolean interactive) {
+        noteInteractiveLocked(interactive, mClocks.elapsedRealtime());
+    }
+
+    public void noteInteractiveLocked(boolean interactive, long elapsedRealtimeMs) {
         if (mInteractive != interactive) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
             mInteractive = interactive;
             if (DEBUG) Slog.v(TAG, "Interactive: " + interactive);
             if (interactive) {
-                mInteractiveTimer.startRunningLocked(elapsedRealtime);
+                mInteractiveTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
-                mInteractiveTimer.stopRunningLocked(elapsedRealtime);
+                mInteractiveTimer.stopRunningLocked(elapsedRealtimeMs);
             }
         }
     }
 
     public void noteConnectivityChangedLocked(int type, String extra) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
+        noteConnectivityChangedLocked(type, extra,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteConnectivityChangedLocked(int type, String extra,
+            long elapsedRealtimeMs, long uptimeMs) {
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
                 extra, type);
         mNumConnectivityChange++;
     }
@@ -4902,15 +5154,19 @@
         uid = mapUid(uid);
         addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
                 uid);
-        getUidStatsLocked(uid).noteMobileRadioApWakeupLocked();
+        getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteMobileRadioApWakeupLocked();
     }
 
     /**
      * Updates the radio power state and returns true if an external stats collection should occur.
      */
     public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        return noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (mMobileRadioPowerState != powerState) {
             long realElapsedRealtimeMs;
             final boolean active =
@@ -4918,31 +5174,31 @@
                             || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
             if (active) {
                 if (uid > 0) {
-                    noteMobileRadioApWakeupLocked(elapsedRealtime, uptime, uid);
+                    noteMobileRadioApWakeupLocked(elapsedRealtimeMs, uptimeMs, uid);
                 }
 
-                mMobileRadioActiveStartTime = realElapsedRealtimeMs = timestampNs / (1000 * 1000);
+                mMobileRadioActiveStartTimeMs = realElapsedRealtimeMs = timestampNs / (1000 * 1000);
                 mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
             } else {
                 realElapsedRealtimeMs = timestampNs / (1000*1000);
-                long lastUpdateTimeMs = mMobileRadioActiveStartTime;
+                long lastUpdateTimeMs = mMobileRadioActiveStartTimeMs;
                 if (realElapsedRealtimeMs < lastUpdateTimeMs) {
                     Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
                             + " is before start time " + lastUpdateTimeMs);
-                    realElapsedRealtimeMs = elapsedRealtime;
-                } else if (realElapsedRealtimeMs < elapsedRealtime) {
-                    mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+                    realElapsedRealtimeMs = elapsedRealtimeMs;
+                } else if (realElapsedRealtimeMs < elapsedRealtimeMs) {
+                    mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtimeMs
                             - realElapsedRealtimeMs);
                 }
                 mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
             }
             if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mMobileRadioPowerState = powerState;
             if (active) {
-                mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
-                mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
+                mMobileRadioActiveTimer.startRunningLocked(elapsedRealtimeMs);
+                mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
                 mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
                 mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
@@ -4954,25 +5210,27 @@
     }
 
     public void notePowerSaveModeLocked(boolean enabled) {
+        notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
         if (mPowerSaveModeEnabled != enabled) {
             int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
             mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
             mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mPowerSaveModeEnabled = enabled;
             if (enabled) {
                 mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
                         + Integer.toHexString(mHistoryCur.states2));
-                mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtime);
+                mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
                 mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
                         + Integer.toHexString(mHistoryCur.states2));
-                mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
+                mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
             }
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
                     enabled
                         ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
@@ -4981,8 +5239,12 @@
     }
 
     public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteDeviceIdleModeLocked(mode, activeReason, activeUid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid,
+            long elapsedRealtimeMs, long uptimeMs) {
         boolean nowIdling = mode == DEVICE_IDLE_MODE_DEEP;
         if (mDeviceIdling && !nowIdling && activeReason == null) {
             // We don't go out of general idling mode until explicitly taken out of
@@ -4996,7 +5258,7 @@
             nowLightIdling = true;
         }
         if (activeReason != null && (mDeviceIdling || mDeviceLightIdling)) {
-            addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ACTIVE,
+            addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_ACTIVE,
                     activeReason, activeUid);
         }
         if (mDeviceIdling != nowIdling || mDeviceLightIdling != nowLightIdling) {
@@ -5012,17 +5274,17 @@
             mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
             mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
             if (nowIdling) {
-                mDeviceIdlingTimer.startRunningLocked(elapsedRealtime);
+                mDeviceIdlingTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
-                mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime);
+                mDeviceIdlingTimer.stopRunningLocked(elapsedRealtimeMs);
             }
         }
         if (mDeviceLightIdling != nowLightIdling) {
             mDeviceLightIdling = nowLightIdling;
             if (nowLightIdling) {
-                mDeviceLightIdlingTimer.startRunningLocked(elapsedRealtime);
+                mDeviceLightIdlingTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
-                mDeviceLightIdlingTimer.stopRunningLocked(elapsedRealtime);
+                mDeviceLightIdlingTimer.stopRunningLocked(elapsedRealtimeMs);
             }
         }
         if (mDeviceIdleMode != mode) {
@@ -5030,24 +5292,24 @@
                     | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode changed to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            long lastDuration = elapsedRealtime - mLastIdleTimeStart;
-            mLastIdleTimeStart = elapsedRealtime;
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            long lastDuration = elapsedRealtimeMs - mLastIdleTimeStartMs;
+            mLastIdleTimeStartMs = elapsedRealtimeMs;
             if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
-                if (lastDuration > mLongestLightIdleTime) {
-                    mLongestLightIdleTime = lastDuration;
+                if (lastDuration > mLongestLightIdleTimeMs) {
+                    mLongestLightIdleTimeMs = lastDuration;
                 }
-                mDeviceIdleModeLightTimer.stopRunningLocked(elapsedRealtime);
+                mDeviceIdleModeLightTimer.stopRunningLocked(elapsedRealtimeMs);
             } else if (mDeviceIdleMode == DEVICE_IDLE_MODE_DEEP) {
-                if (lastDuration > mLongestFullIdleTime) {
-                    mLongestFullIdleTime = lastDuration;
+                if (lastDuration > mLongestFullIdleTimeMs) {
+                    mLongestFullIdleTimeMs = lastDuration;
                 }
-                mDeviceIdleModeFullTimer.stopRunningLocked(elapsedRealtime);
+                mDeviceIdleModeFullTimer.stopRunningLocked(elapsedRealtimeMs);
             }
             if (mode == DEVICE_IDLE_MODE_LIGHT) {
-                mDeviceIdleModeLightTimer.startRunningLocked(elapsedRealtime);
+                mDeviceIdleModeLightTimer.startRunningLocked(elapsedRealtimeMs);
             } else if (mode == DEVICE_IDLE_MODE_DEEP) {
-                mDeviceIdleModeFullTimer.startRunningLocked(elapsedRealtime);
+                mDeviceIdleModeFullTimer.startRunningLocked(elapsedRealtimeMs);
             }
             mDeviceIdleMode = mode;
             FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mode);
@@ -5055,10 +5317,14 @@
     }
 
     public void notePackageInstalledLocked(String pkgName, long versionCode) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        notePackageInstalledLocked(pkgName, versionCode,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePackageInstalledLocked(String pkgName, long versionCode,
+            long elapsedRealtimeMs, long uptimeMs) {
         // XXX need to figure out what to do with long version codes.
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_INSTALLED,
                 pkgName, (int)versionCode);
         PackageChange pc = new PackageChange();
         pc.mPackageName = pkgName;
@@ -5068,10 +5334,13 @@
     }
 
     public void notePackageUninstalledLocked(String pkgName) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
-                pkgName, 0);
+        notePackageUninstalledLocked(pkgName, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePackageUninstalledLocked(String pkgName,
+            long elapsedRealtimeMs, long uptimeMs) {
+        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
+                HistoryItem.EVENT_PACKAGE_UNINSTALLED, pkgName, 0);
         PackageChange pc = new PackageChange();
         pc.mPackageName = pkgName;
         pc.mUpdate = true;
@@ -5086,42 +5355,49 @@
     }
 
     void stopAllGpsSignalQualityTimersLocked(int except) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
+        stopAllGpsSignalQualityTimersLocked(except, mClocks.elapsedRealtime());
+    }
+
+    void stopAllGpsSignalQualityTimersLocked(int except, long elapsedRealtimeMs) {
         for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             if (i == except) {
                 continue;
             }
             while (mGpsSignalQualityTimer[i].isRunningLocked()) {
-                mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtime);
+                mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtimeMs);
             }
         }
     }
 
     @UnsupportedAppUsage
     public void notePhoneOnLocked() {
+        notePhoneOnLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePhoneOnLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (!mPhoneOn) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states2 |= HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mPhoneOn = true;
-            mPhoneOnTimer.startRunningLocked(elapsedRealtime);
+            mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
     }
 
     @UnsupportedAppUsage
     public void notePhoneOffLocked() {
+        notePhoneOffLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePhoneOffLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mPhoneOn) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states2 &= ~HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mPhoneOn = false;
-            mPhoneOnTimer.stopRunningLocked(elapsedRealtime);
+            mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
     }
 
@@ -5133,7 +5409,8 @@
             public void onReceive(Context context, Intent intent) {
                 final boolean state = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
                 synchronized (BatteryStatsImpl.this) {
-                    noteUsbConnectionStateLocked(state);
+                    noteUsbConnectionStateLocked(state, mClocks.elapsedRealtime(),
+                            mClocks.uptimeMillis());
                 }
             }
         }, usbStateFilter);
@@ -5142,12 +5419,14 @@
                 final Intent usbState = context.registerReceiver(null, usbStateFilter);
                 final boolean initState = usbState != null && usbState.getBooleanExtra(
                         UsbManager.USB_CONNECTED, false);
-                noteUsbConnectionStateLocked(initState);
+                noteUsbConnectionStateLocked(initState, mClocks.elapsedRealtime(),
+                        mClocks.uptimeMillis());
             }
         }
     }
 
-    private void noteUsbConnectionStateLocked(boolean connected) {
+    private void noteUsbConnectionStateLocked(boolean connected, long elapsedRealtimeMs,
+            long uptimeMs) {
         int newState = connected ? USB_DATA_CONNECTED : USB_DATA_DISCONNECTED;
         if (mUsbDataState != newState) {
             mUsbDataState = newState;
@@ -5156,18 +5435,17 @@
             } else {
                 mHistoryCur.states2 &= ~HistoryItem.STATE2_USB_DATA_LINK_FLAG;
             }
-            addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
     }
 
-    void stopAllPhoneSignalStrengthTimersLocked(int except) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
+    void stopAllPhoneSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
         for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
             if (i == except) {
                 continue;
             }
             while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) {
-                mPhoneSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtime);
+                mPhoneSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtimeMs);
             }
         }
     }
@@ -5185,7 +5463,8 @@
         return state;
     }
 
-    private void updateAllPhoneStateLocked(int state, int simState, int strengthBin) {
+    private void updateAllPhoneStateLocked(int state, int simState, int strengthBin,
+            long elapsedRealtimeMs, long uptimeMs) {
         boolean scanning = false;
         boolean newHistory = false;
 
@@ -5193,9 +5472,6 @@
         mPhoneSimStateRaw = simState;
         mPhoneSignalStrengthBinRaw = strengthBin;
 
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
-
         if (simState == TelephonyManager.SIM_STATE_ABSENT) {
             // In this case we will always be STATE_OUT_OF_SERVICE, so need
             // to infer that we are scanning from other data.
@@ -5223,7 +5499,7 @@
                 newHistory = true;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
                         + Integer.toHexString(mHistoryCur.states));
-                mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtime);
+                mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtimeMs);
                 FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
                         simState, strengthBin);
             }
@@ -5236,7 +5512,7 @@
                 if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
                         + Integer.toHexString(mHistoryCur.states));
                 newHistory = true;
-                mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtime);
+                mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtimeMs);
                 FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
                         simState, strengthBin);
             }
@@ -5254,13 +5530,14 @@
         if (mPhoneSignalStrengthBin != strengthBin) {
             if (mPhoneSignalStrengthBin >= 0) {
                 mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(
-                        elapsedRealtime);
+                        elapsedRealtimeMs);
             }
             if (strengthBin >= 0) {
                 if (!mPhoneSignalStrengthsTimer[strengthBin].isRunningLocked()) {
-                    mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
+                    mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
                 }
-                mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
+                mHistoryCur.states =
+                        (mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
                         | (strengthBin << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
                 if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
                         + Integer.toHexString(mHistoryCur.states));
@@ -5268,13 +5545,13 @@
                 FrameworkStatsLog.write(
                         FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
             } else {
-                stopAllPhoneSignalStrengthTimersLocked(-1);
+                stopAllPhoneSignalStrengthTimersLocked(-1, elapsedRealtimeMs);
             }
             mPhoneSignalStrengthBin = strengthBin;
         }
 
         if (newHistory) {
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
     }
 
@@ -5283,18 +5560,37 @@
      * @param state phone state from ServiceState.getState()
      */
     public void notePhoneStateLocked(int state, int simState) {
-        updateAllPhoneStateLocked(state, simState, mPhoneSignalStrengthBinRaw);
+        notePhoneStateLocked(state, simState, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePhoneStateLocked(int state, int simState,
+            long elapsedRealtimeMs, long uptimeMs) {
+        updateAllPhoneStateLocked(state, simState, mPhoneSignalStrengthBinRaw,
+                elapsedRealtimeMs, uptimeMs);
     }
 
     @UnsupportedAppUsage
     public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
+        notePhoneSignalStrengthLocked(signalStrength,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePhoneSignalStrengthLocked(SignalStrength signalStrength,
+            long elapsedRealtimeMs, long uptimeMs) {
         // Bin the strength.
         int bin = signalStrength.getLevel();
-        updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, bin);
+        updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, bin,
+                elapsedRealtimeMs, uptimeMs);
     }
 
     @UnsupportedAppUsage
     public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType) {
+        notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType,
+            long elapsedRealtimeMs, long uptimeMs) {
         // BatteryStats uses 0 to represent no network type.
         // Telephony does not have a concept of no network type, and uses 0 to represent unknown.
         // Unknown is included in DATA_CONNECTION_OTHER.
@@ -5318,312 +5614,374 @@
         }
         if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
                     | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
-                        elapsedRealtime);
+                        elapsedRealtimeMs);
             }
             mPhoneDataConnectionType = bin;
-            mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtime);
+            mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtimeMs);
         }
     }
 
     public void noteWifiOnLocked() {
+        noteWifiOnLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (!mWifiOn) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mWifiOn = true;
-            mWifiOnTimer.startRunningLocked(elapsedRealtime);
+            mWifiOnTimer.startRunningLocked(elapsedRealtimeMs);
             scheduleSyncExternalStatsLocked("wifi-off", ExternalStatsSync.UPDATE_WIFI);
         }
     }
 
     public void noteWifiOffLocked() {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteWifiOffLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiOffLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mWifiOn) {
             mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mWifiOn = false;
-            mWifiOnTimer.stopRunningLocked(elapsedRealtime);
+            mWifiOnTimer.stopRunningLocked(elapsedRealtimeMs);
             scheduleSyncExternalStatsLocked("wifi-on", ExternalStatsSync.UPDATE_WIFI);
         }
     }
 
     @UnsupportedAppUsage
     public void noteAudioOnLocked(int uid) {
+        noteAudioOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteAudioOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mAudioOnNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mAudioOnTimer.startRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mAudioOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
         mAudioOnNesting++;
-        getUidStatsLocked(uid).noteAudioTurnedOnLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteAudioTurnedOnLocked(elapsedRealtimeMs);
     }
 
     @UnsupportedAppUsage
     public void noteAudioOffLocked(int uid) {
+        noteAudioOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteAudioOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (mAudioOnNesting == 0) {
             return;
         }
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (--mAudioOnNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mAudioOnTimer.stopRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteAudioTurnedOffLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteAudioTurnedOffLocked(elapsedRealtimeMs);
     }
 
     @UnsupportedAppUsage
     public void noteVideoOnLocked(int uid) {
+        noteVideoOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteVideoOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mVideoOnNesting == 0) {
             mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mVideoOnTimer.startRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mVideoOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
         mVideoOnNesting++;
-        getUidStatsLocked(uid).noteVideoTurnedOnLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteVideoTurnedOnLocked(elapsedRealtimeMs);
     }
 
     @UnsupportedAppUsage
     public void noteVideoOffLocked(int uid) {
+        noteVideoOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteVideoOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (mVideoOnNesting == 0) {
             return;
         }
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (--mVideoOnNesting == 0) {
             mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mVideoOnTimer.stopRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteVideoTurnedOffLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteVideoTurnedOffLocked(elapsedRealtimeMs);
     }
 
     public void noteResetAudioLocked() {
+        noteResetAudioLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteResetAudioLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mAudioOnNesting > 0) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mAudioOnNesting = 0;
             mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mAudioOnTimer.stopAllRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mAudioOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
             for (int i=0; i<mUidStats.size(); i++) {
                 BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-                uid.noteResetAudioLocked(elapsedRealtime);
+                uid.noteResetAudioLocked(elapsedRealtimeMs);
             }
         }
     }
 
     public void noteResetVideoLocked() {
+        noteResetVideoLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteResetVideoLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mVideoOnNesting > 0) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mVideoOnNesting = 0;
             mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mVideoOnTimer.stopAllRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mVideoOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
             for (int i=0; i<mUidStats.size(); i++) {
                 BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-                uid.noteResetVideoLocked(elapsedRealtime);
+                uid.noteResetVideoLocked(elapsedRealtimeMs);
             }
         }
     }
 
     public void noteActivityResumedLocked(int uid) {
+        noteActivityResumedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteActivityResumedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        getUidStatsLocked(uid).noteActivityResumedLocked(mClocks.elapsedRealtime());
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteActivityResumedLocked(elapsedRealtimeMs);
     }
 
     public void noteActivityPausedLocked(int uid) {
+        noteActivityPausedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteActivityPausedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        getUidStatsLocked(uid).noteActivityPausedLocked(mClocks.elapsedRealtime());
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteActivityPausedLocked(elapsedRealtimeMs);
     }
 
     public void noteVibratorOnLocked(int uid, long durationMillis) {
+        noteVibratorOnLocked(uid, durationMillis,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteVibratorOnLocked(int uid, long durationMillis,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteVibratorOnLocked(durationMillis, elapsedRealtimeMs);
     }
 
     public void noteVibratorOffLocked(int uid) {
+        noteVibratorOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteVibratorOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        getUidStatsLocked(uid).noteVibratorOffLocked();
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteVibratorOffLocked(elapsedRealtimeMs);
     }
 
     public void noteFlashlightOnLocked(int uid) {
+        noteFlashlightOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteFlashlightOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mFlashlightOnNesting++ == 0) {
             mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mFlashlightOnTimer.startRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteFlashlightTurnedOnLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
     }
 
     public void noteFlashlightOffLocked(int uid) {
+        noteFlashlightOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteFlashlightOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (mFlashlightOnNesting == 0) {
             return;
         }
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (--mFlashlightOnNesting == 0) {
             mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mFlashlightOnTimer.stopRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteFlashlightTurnedOffLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
     }
 
     public void noteCameraOnLocked(int uid) {
+        noteCameraOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteCameraOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mCameraOnNesting++ == 0) {
             mHistoryCur.states2 |= HistoryItem.STATE2_CAMERA_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Camera on to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mCameraOnTimer.startRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mCameraOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteCameraTurnedOnLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteCameraTurnedOnLocked(elapsedRealtimeMs);
     }
 
     public void noteCameraOffLocked(int uid) {
+        noteCameraOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteCameraOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (mCameraOnNesting == 0) {
             return;
         }
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (--mCameraOnNesting == 0) {
             mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mCameraOnTimer.stopRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteCameraTurnedOffLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteCameraTurnedOffLocked(elapsedRealtimeMs);
     }
 
     public void noteResetCameraLocked() {
+        noteResetCameraLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteResetCameraLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mCameraOnNesting > 0) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mCameraOnNesting = 0;
             mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mCameraOnTimer.stopAllRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mCameraOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
             for (int i=0; i<mUidStats.size(); i++) {
                 BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-                uid.noteResetCameraLocked(elapsedRealtime);
+                uid.noteResetCameraLocked(elapsedRealtimeMs);
             }
         }
     }
 
     public void noteResetFlashlightLocked() {
+        noteResetFlashlightLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteResetFlashlightLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mFlashlightOnNesting > 0) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mFlashlightOnNesting = 0;
             mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mFlashlightOnTimer.stopAllRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mFlashlightOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
             for (int i=0; i<mUidStats.size(); i++) {
                 BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-                uid.noteResetFlashlightLocked(elapsedRealtime);
+                uid.noteResetFlashlightLocked(elapsedRealtimeMs);
             }
         }
     }
 
     private void noteBluetoothScanStartedLocked(WorkChain workChain, int uid,
-            boolean isUnoptimized) {
+            boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) {
         uid = getAttributionUid(uid, workChain);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mBluetoothScanNesting == 0) {
             mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mBluetoothScanTimer.startRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mBluetoothScanTimer.startRunningLocked(elapsedRealtimeMs);
         }
         mBluetoothScanNesting++;
-        getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime, isUnoptimized);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteBluetoothScanStartedLocked(elapsedRealtimeMs, isUnoptimized);
     }
 
     public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
+        noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
+            long elapsedRealtimeMs, long uptimeMs) {
         final int N = ws.size();
         for (int i = 0; i < N; i++) {
-            noteBluetoothScanStartedLocked(null, ws.getUid(i), isUnoptimized);
+            noteBluetoothScanStartedLocked(null, ws.getUid(i), isUnoptimized,
+                    elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
         if (workChains != null) {
             for (int i = 0; i < workChains.size(); ++i) {
-                noteBluetoothScanStartedLocked(workChains.get(i), -1, isUnoptimized);
+                noteBluetoothScanStartedLocked(workChains.get(i), -1, isUnoptimized,
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     private void noteBluetoothScanStoppedLocked(WorkChain workChain, int uid,
-            boolean isUnoptimized) {
+            boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) {
         uid = getAttributionUid(uid, workChain);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         mBluetoothScanNesting--;
         if (mBluetoothScanNesting == 0) {
             mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan stopped for: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mBluetoothScanTimer.stopRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime, isUnoptimized);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteBluetoothScanStoppedLocked(elapsedRealtimeMs, isUnoptimized);
     }
 
     private int getAttributionUid(int uid, WorkChain workChain) {
@@ -5635,41 +5993,58 @@
     }
 
     public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
+        noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
+            long elapsedRealtimeMs, long uptimeMs) {
         final int N = ws.size();
         for (int i = 0; i < N; i++) {
-            noteBluetoothScanStoppedLocked(null, ws.getUid(i), isUnoptimized);
+            noteBluetoothScanStoppedLocked(null, ws.getUid(i), isUnoptimized,
+                    elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
         if (workChains != null) {
             for (int i = 0; i < workChains.size(); ++i) {
-                noteBluetoothScanStoppedLocked(workChains.get(i), -1, isUnoptimized);
+                noteBluetoothScanStoppedLocked(workChains.get(i), -1, isUnoptimized,
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteResetBluetoothScanLocked() {
+        noteResetBluetoothScanLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteResetBluetoothScanLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (mBluetoothScanNesting > 0) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mBluetoothScanNesting = 0;
             mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "BLE can stopped for: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+            mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
             for (int i=0; i<mUidStats.size(); i++) {
                 BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
-                uid.noteResetBluetoothScanLocked(elapsedRealtime);
+                uid.noteResetBluetoothScanLocked(elapsedRealtimeMs);
             }
         }
     }
 
     public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults) {
+        noteBluetoothScanResultsFromSourceLocked(ws, numNewResults,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults,
+            long elapsedRealtimeMs, long uptimeMs) {
         final int N = ws.size();
         for (int i = 0; i < N; i++) {
             int uid = mapUid(ws.getUid(i));
-            getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteBluetoothScanResultsLocked(numNewResults);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -5677,7 +6052,8 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain wc = workChains.get(i);
                 int uid = mapUid(wc.getAttributionUid());
-                getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .noteBluetoothScanResultsLocked(numNewResults);
             }
         }
     }
@@ -5687,55 +6063,62 @@
         uid = mapUid(uid);
         addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
                 uid);
-        getUidStatsLocked(uid).noteWifiRadioApWakeupLocked();
+        getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteWifiRadioApWakeupLocked();
     }
 
     public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteWifiRadioPowerState(powerState, timestampNs, uid,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (mWifiRadioPowerState != powerState) {
             final boolean active =
                     powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
                             || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
             if (active) {
                 if (uid > 0) {
-                    noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid);
+                    noteWifiRadioApWakeupLocked(elapsedRealtimeMs, uptimeMs, uid);
                 }
                 mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
-                mWifiActiveTimer.startRunningLocked(elapsedRealtime);
+                mWifiActiveTimer.startRunningLocked(elapsedRealtimeMs);
             } else {
                 mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
-                mWifiActiveTimer.stopRunningLocked(
-                    timestampNs / (1000 * 1000));
+                mWifiActiveTimer.stopRunningLocked(timestampNs / (1000 * 1000));
             }
             if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mWifiRadioPowerState = powerState;
         }
     }
 
     public void noteWifiRunningLocked(WorkSource ws) {
+        noteWifiRunningLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiRunningLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
         if (!mGlobalWifiRunning) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mGlobalWifiRunning = true;
-            mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtime);
+            mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
             int N = ws.size();
             for (int i=0; i<N; i++) {
                 int uid = mapUid(ws.getUid(i));
-                getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .noteWifiRunningLocked(elapsedRealtimeMs);
             }
 
             List<WorkChain> workChains = ws.getWorkChains();
             if (workChains != null) {
                 for (int i = 0; i < workChains.size(); ++i) {
                     int uid = mapUid(workChains.get(i).getAttributionUid());
-                    getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
+                    getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                            .noteWifiRunningLocked(elapsedRealtimeMs);
                 }
             }
 
@@ -5746,33 +6129,42 @@
     }
 
     public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs) {
+        noteWifiRunningChangedLocked(oldWs, newWs,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (mGlobalWifiRunning) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
             int N = oldWs.size();
             for (int i=0; i<N; i++) {
                 int uid = mapUid(oldWs.getUid(i));
-                getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .noteWifiStoppedLocked(elapsedRealtimeMs);
             }
 
             List<WorkChain> workChains = oldWs.getWorkChains();
             if (workChains != null) {
                 for (int i = 0; i < workChains.size(); ++i) {
                     int uid = mapUid(workChains.get(i).getAttributionUid());
-                    getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
+                    getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                            .noteWifiStoppedLocked(elapsedRealtimeMs);
                 }
             }
 
             N = newWs.size();
             for (int i=0; i<N; i++) {
                 int uid = mapUid(newWs.getUid(i));
-                getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .noteWifiRunningLocked(elapsedRealtimeMs);
             }
 
             workChains = newWs.getWorkChains();
             if (workChains != null) {
                 for (int i = 0; i < workChains.size(); ++i) {
                     int uid = mapUid(workChains.get(i).getAttributionUid());
-                    getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
+                    getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                            .noteWifiRunningLocked(elapsedRealtimeMs);
                 }
             }
         } else {
@@ -5781,26 +6173,30 @@
     }
 
     public void noteWifiStoppedLocked(WorkSource ws) {
+        noteWifiStoppedLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiStoppedLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
         if (mGlobalWifiRunning) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             mGlobalWifiRunning = false;
-            mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtime);
+            mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtimeMs);
             int N = ws.size();
             for (int i=0; i<N; i++) {
                 int uid = mapUid(ws.getUid(i));
-                getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .noteWifiStoppedLocked(elapsedRealtimeMs);
             }
 
             List<WorkChain> workChains = ws.getWorkChains();
             if (workChains != null) {
                 for (int i = 0; i < workChains.size(); ++i) {
                     int uid = mapUid(workChains.get(i).getAttributionUid());
-                    getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
+                    getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                            .noteWifiStoppedLocked(elapsedRealtimeMs);
                 }
             }
 
@@ -5811,71 +6207,79 @@
     }
 
     public void noteWifiStateLocked(int wifiState, String accessPoint) {
+        noteWifiStateLocked(wifiState, accessPoint, mClocks.elapsedRealtime());
+    }
+
+    public void noteWifiStateLocked(int wifiState, String accessPoint, long elapsedRealtimeMs) {
         if (DEBUG) Log.i(TAG, "WiFi state -> " + wifiState);
         if (mWifiState != wifiState) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
             if (mWifiState >= 0) {
-                mWifiStateTimer[mWifiState].stopRunningLocked(elapsedRealtime);
+                mWifiStateTimer[mWifiState].stopRunningLocked(elapsedRealtimeMs);
             }
             mWifiState = wifiState;
-            mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime);
+            mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtimeMs);
             scheduleSyncExternalStatsLocked("wifi-state", ExternalStatsSync.UPDATE_WIFI);
         }
     }
 
     public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth) {
+        noteWifiSupplicantStateChangedLocked(supplState, failedAuth,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG) Log.i(TAG, "WiFi suppl state -> " + supplState);
         if (mWifiSupplState != supplState) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             if (mWifiSupplState >= 0) {
-                mWifiSupplStateTimer[mWifiSupplState].stopRunningLocked(elapsedRealtime);
+                mWifiSupplStateTimer[mWifiSupplState].stopRunningLocked(elapsedRealtimeMs);
             }
             mWifiSupplState = supplState;
-            mWifiSupplStateTimer[supplState].startRunningLocked(elapsedRealtime);
+            mWifiSupplStateTimer[supplState].startRunningLocked(elapsedRealtimeMs);
             mHistoryCur.states2 =
                     (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
                     | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Wifi suppl state " + supplState + " to: "
                     + Integer.toHexString(mHistoryCur.states2));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
     }
 
-    void stopAllWifiSignalStrengthTimersLocked(int except) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
+    void stopAllWifiSignalStrengthTimersLocked(int except, long elapsedRealtimeMs) {
         for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             if (i == except) {
                 continue;
             }
             while (mWifiSignalStrengthsTimer[i].isRunningLocked()) {
-                mWifiSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtime);
+                mWifiSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtimeMs);
             }
         }
     }
 
     public void noteWifiRssiChangedLocked(int newRssi) {
+        noteWifiRssiChangedLocked(newRssi, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiRssiChangedLocked(int newRssi, long elapsedRealtimeMs, long uptimeMs) {
         int strengthBin = WifiManager.calculateSignalLevel(newRssi, NUM_WIFI_SIGNAL_STRENGTH_BINS);
         if (DEBUG) Log.i(TAG, "WiFi rssi -> " + newRssi + " bin=" + strengthBin);
         if (mWifiSignalStrengthBin != strengthBin) {
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
             if (mWifiSignalStrengthBin >= 0) {
                 mWifiSignalStrengthsTimer[mWifiSignalStrengthBin].stopRunningLocked(
-                        elapsedRealtime);
+                        elapsedRealtimeMs);
             }
             if (strengthBin >= 0) {
                 if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
-                    mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
+                    mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
                 }
                 mHistoryCur.states2 =
                         (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
                         | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
                 if (DEBUG_HISTORY) Slog.v(TAG, "Wifi signal strength " + strengthBin + " to: "
                         + Integer.toHexString(mHistoryCur.states2));
-                addHistoryRecordLocked(elapsedRealtime, uptime);
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             } else {
-                stopAllWifiSignalStrengthTimersLocked(-1);
+                stopAllWifiSignalStrengthTimersLocked(-1, elapsedRealtimeMs);
             }
             mWifiSignalStrengthBin = strengthBin;
         }
@@ -5885,121 +6289,155 @@
 
     @UnsupportedAppUsage
     public void noteFullWifiLockAcquiredLocked(int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteFullWifiLockAcquiredLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteFullWifiLockAcquiredLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (mWifiFullLockNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
         mWifiFullLockNesting++;
-        getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteFullWifiLockAcquiredLocked(elapsedRealtimeMs);
     }
 
     @UnsupportedAppUsage
     public void noteFullWifiLockReleasedLocked(int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteFullWifiLockReleasedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteFullWifiLockReleasedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         mWifiFullLockNesting--;
         if (mWifiFullLockNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
-        getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteFullWifiLockReleasedLocked(elapsedRealtimeMs);
     }
 
     int mWifiScanNesting = 0;
 
     public void noteWifiScanStartedLocked(int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteWifiScanStartedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiScanStartedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         if (mWifiScanNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
         mWifiScanNesting++;
-        getUidStatsLocked(uid).noteWifiScanStartedLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteWifiScanStartedLocked(elapsedRealtimeMs);
     }
 
     public void noteWifiScanStoppedLocked(int uid) {
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
+        noteWifiScanStoppedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         mWifiScanNesting--;
         if (mWifiScanNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
-        getUidStatsLocked(uid).noteWifiScanStoppedLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteWifiScanStoppedLocked(elapsedRealtimeMs);
     }
 
     public void noteWifiBatchedScanStartedLocked(int uid, int csph) {
+        noteWifiBatchedScanStartedLocked(uid, csph,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiBatchedScanStartedLocked(int uid, int csph,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        getUidStatsLocked(uid).noteWifiBatchedScanStartedLocked(csph, elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteWifiBatchedScanStartedLocked(csph, elapsedRealtimeMs);
     }
 
     public void noteWifiBatchedScanStoppedLocked(int uid) {
+        noteWifiBatchedScanStoppedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiBatchedScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        getUidStatsLocked(uid).noteWifiBatchedScanStoppedLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteWifiBatchedScanStoppedLocked(elapsedRealtimeMs);
     }
 
     int mWifiMulticastNesting = 0;
 
     @UnsupportedAppUsage
     public void noteWifiMulticastEnabledLocked(int uid) {
+        noteWifiMulticastEnabledLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiMulticastEnabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         if (mWifiMulticastNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
 
             // Start Wifi Multicast overall timer
             if (!mWifiMulticastWakelockTimer.isRunningLocked()) {
                 if (DEBUG_HISTORY) Slog.v(TAG, "WiFi Multicast Overall Timer Started");
-                mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtime);
+                mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtimeMs);
             }
         }
         mWifiMulticastNesting++;
-        getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteWifiMulticastEnabledLocked(elapsedRealtimeMs);
     }
 
     @UnsupportedAppUsage
     public void noteWifiMulticastDisabledLocked(int uid) {
+        noteWifiMulticastDisabledLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiMulticastDisabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         mWifiMulticastNesting--;
         if (mWifiMulticastNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
 
             // Stop Wifi Multicast overall timer
             if (mWifiMulticastWakelockTimer.isRunningLocked()) {
                 if (DEBUG_HISTORY) Slog.v(TAG, "Multicast Overall Timer Stopped");
-                mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtime);
+                mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtimeMs);
             }
         }
-        getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
+        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                .noteWifiMulticastDisabledLocked(elapsedRealtimeMs);
     }
 
     public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+        noteFullWifiLockAcquiredFromSourceLocked(ws,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws,
+            long elapsedRealtimeMs, long uptimeMs) {
         int N = ws.size();
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.getUid(i));
-            noteFullWifiLockAcquiredLocked(uid);
+            noteFullWifiLockAcquiredLocked(uid, elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6007,16 +6445,22 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain workChain = workChains.get(i);
                 final int uid = mapUid(workChain.getAttributionUid());
-                noteFullWifiLockAcquiredLocked(uid);
+                noteFullWifiLockAcquiredLocked(uid, elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
+        noteFullWifiLockReleasedFromSourceLocked(ws,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws,
+            long elapsedRealtimeMs, long uptimeMs) {
         int N = ws.size();
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.getUid(i));
-            noteFullWifiLockReleasedLocked(uid);
+            noteFullWifiLockReleasedLocked(uid, elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6024,16 +6468,21 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain workChain = workChains.get(i);
                 final int uid = mapUid(workChain.getAttributionUid());
-                noteFullWifiLockReleasedLocked(uid);
+                noteFullWifiLockReleasedLocked(uid, elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteWifiScanStartedFromSourceLocked(WorkSource ws) {
+        noteWifiScanStartedFromSourceLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiScanStartedFromSourceLocked(WorkSource ws,
+            long elapsedRealtimeMs, long uptimeMs) {
         int N = ws.size();
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.getUid(i));
-            noteWifiScanStartedLocked(uid);
+            noteWifiScanStartedLocked(uid, elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6041,16 +6490,21 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain workChain = workChains.get(i);
                 final int uid = mapUid(workChain.getAttributionUid());
-                noteWifiScanStartedLocked(uid);
+                noteWifiScanStartedLocked(uid, elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) {
+        noteWifiScanStoppedFromSourceLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiScanStoppedFromSourceLocked(WorkSource ws,
+            long elapsedRealtimeMs, long uptimeMs) {
         int N = ws.size();
         for (int i=0; i<N; i++) {
             final int uid = mapUid(ws.getUid(i));
-            noteWifiScanStoppedLocked(uid);
+            noteWifiScanStoppedLocked(uid, elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -6058,35 +6512,48 @@
             for (int i = 0; i < workChains.size(); ++i) {
                 final WorkChain workChain = workChains.get(i);
                 final int uid = mapUid(workChain.getAttributionUid());
-                noteWifiScanStoppedLocked(uid);
+                noteWifiScanStoppedLocked(uid, elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph) {
+        noteWifiBatchedScanStartedFromSourceLocked(ws, csph,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph,
+            long elapsedRealtimeMs, long uptimeMs) {
         int N = ws.size();
         for (int i=0; i<N; i++) {
-            noteWifiBatchedScanStartedLocked(ws.getUid(i), csph);
+            noteWifiBatchedScanStartedLocked(ws.getUid(i), csph, elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
         if (workChains != null) {
             for (int i = 0; i < workChains.size(); ++i) {
-                noteWifiBatchedScanStartedLocked(workChains.get(i).getAttributionUid(), csph);
+                noteWifiBatchedScanStartedLocked(workChains.get(i).getAttributionUid(), csph,
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
 
     public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws) {
+        noteWifiBatchedScanStoppedFromSourceLocked(ws,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+    public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws,
+            long elapsedRealtimeMs, long uptimeMs) {
         int N = ws.size();
         for (int i=0; i<N; i++) {
-            noteWifiBatchedScanStoppedLocked(ws.getUid(i));
+            noteWifiBatchedScanStoppedLocked(ws.getUid(i), elapsedRealtimeMs, uptimeMs);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
         if (workChains != null) {
             for (int i = 0; i < workChains.size(); ++i) {
-                noteWifiBatchedScanStoppedLocked(workChains.get(i).getAttributionUid());
+                noteWifiBatchedScanStoppedLocked(workChains.get(i).getAttributionUid(),
+                        elapsedRealtimeMs, uptimeMs);
             }
         }
     }
@@ -6147,9 +6614,16 @@
      */
     public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
             Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
+        noteBinderCallStats(workSourceUid, incrementalCallCount, callStats, binderThreadNativeTids,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
+            Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids,
+            long elapsedRealtimeMs, long uptimeMs) {
         synchronized (this) {
-            getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
-                    callStats);
+            getUidStatsLocked(workSourceUid, elapsedRealtimeMs, uptimeMs)
+                    .noteBinderCallStatsLocked(incrementalCallCount, callStats);
             mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
         }
     }
@@ -6182,17 +6656,17 @@
         for (int i = 0; i < mUidStats.size(); i++) {
             Uid uid = mUidStats.valueAt(i);
 
-            long totalTimeForUid = 0;
+            long totalTimeForUidUs = 0;
             int totalCallCountForUid = 0;
             ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
             for (int j = binderCallStats.size() - 1; j >= 0; j--) {
                 BinderCallStats stats = binderCallStats.valueAt(j);
                 totalCallCountForUid += stats.callCount;
                 if (stats.recordedCallCount > 0) {
-                    totalTimeForUid +=
+                    totalTimeForUidUs +=
                             stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount;
                 } else if (totalRecordedCallCount > 0) {
-                    totalTimeForUid +=
+                    totalTimeForUidUs +=
                             stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount;
                 }
             }
@@ -6200,13 +6674,13 @@
             if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) {
                 // Estimate remaining calls, which were not tracked because of binder call
                 // stats sampling
-                totalTimeForUid +=
+                totalTimeForUidUs +=
                         (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros
                                 / totalRecordedCallCount;
             }
 
-            uid.mSystemServiceTimeUs = totalTimeForUid;
-            totalSystemServiceTimeMicros += totalTimeForUid;
+            uid.mSystemServiceTimeUs = totalTimeForUidUs;
+            totalSystemServiceTimeMicros += totalTimeForUidUs;
         }
 
         for (int i = 0; i < mUidStats.size(); i++) {
@@ -6296,9 +6770,9 @@
     @Override public long getLongestDeviceIdleModeTime(int mode) {
         switch (mode) {
             case DEVICE_IDLE_MODE_LIGHT:
-                return mLongestLightIdleTime;
+                return mLongestLightIdleTimeMs;
             case DEVICE_IDLE_MODE_DEEP:
-                return mLongestFullIdleTime;
+                return mLongestFullIdleTimeMs;
         }
         return 0;
     }
@@ -6328,12 +6802,12 @@
     }
 
     @Override public long getGpsSignalQualityTime(int strengthBin,
-        long elapsedRealtimeUs, int which) {
+            long elapsedRealtimeUs, int which) {
         if (strengthBin < 0 || strengthBin >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
             return 0;
         }
         return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked(
-            elapsedRealtimeUs, which);
+                elapsedRealtimeUs, which);
     }
 
     @Override public long getGpsBatteryDrainMaMs() {
@@ -6344,11 +6818,11 @@
         }
         double energyUsedMaMs = 0.0;
         final int which = STATS_SINCE_CHARGED;
-        final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
+        final long rawRealtimeUs = SystemClock.elapsedRealtime() * 1000;
         for(int i=0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
             energyUsedMaMs
-                += mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i)
-                * (getGpsSignalQualityTime(i, rawRealtime, which) / 1000);
+                    += mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i)
+                    * (getGpsSignalQualityTime(i, rawRealtimeUs, which) / 1000);
         }
         return (long) energyUsedMaMs;
     }
@@ -6562,18 +7036,18 @@
     }
 
     @Override public long getStartClockTime() {
-        final long currentTime = System.currentTimeMillis();
-        if ((currentTime > MILLISECONDS_IN_YEAR
-                && mStartClockTime < (currentTime - MILLISECONDS_IN_YEAR))
-                || (mStartClockTime > currentTime)) {
+        final long currentTimeMs = System.currentTimeMillis();
+        if ((currentTimeMs > MILLISECONDS_IN_YEAR
+                && mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
+                || (mStartClockTimeMs > currentTimeMs)) {
             // If the start clock time has changed by more than a year, then presumably
             // the previous time was completely bogus.  So we are going to figure out a
             // new time based on how much time has elapsed since we started counting.
-            recordCurrentTimeChangeLocked(currentTime, mClocks.elapsedRealtime(),
+            recordCurrentTimeChangeLocked(currentTimeMs, mClocks.elapsedRealtime(),
                     mClocks.uptimeMillis());
-            return currentTime - (mClocks.elapsedRealtime() - (mRealtimeStart / 1000));
+            return currentTimeMs - (mClocks.elapsedRealtime() - (mRealtimeStartUs / 1000));
         }
-        return mStartClockTime;
+        return mStartClockTimeMs;
     }
 
     @Override public String getStartPlatformVersion() {
@@ -6597,29 +7071,32 @@
         return mUidStats;
     }
 
-    private static <T extends TimeBaseObs> boolean resetIfNotNull(T t, boolean detachIfReset) {
+    private static <T extends TimeBaseObs> boolean resetIfNotNull(T t, boolean detachIfReset,
+            long elapsedRealtimeUs) {
         if (t != null) {
-            return t.reset(detachIfReset);
+            return t.reset(detachIfReset, elapsedRealtimeUs);
         }
         return true;
     }
 
-    private static <T extends TimeBaseObs> boolean resetIfNotNull(T[] t, boolean detachIfReset) {
+    private static <T extends TimeBaseObs> boolean resetIfNotNull(T[] t, boolean detachIfReset,
+            long elapsedRealtimeUs) {
         if (t != null) {
             boolean ret = true;
             for (int i = 0; i < t.length; i++) {
-                ret &= resetIfNotNull(t[i], detachIfReset);
+                ret &= resetIfNotNull(t[i], detachIfReset, elapsedRealtimeUs);
             }
             return ret;
         }
         return true;
     }
 
-    private static <T extends TimeBaseObs> boolean resetIfNotNull(T[][] t, boolean detachIfReset) {
+    private static <T extends TimeBaseObs> boolean resetIfNotNull(T[][] t, boolean detachIfReset,
+            long elapsedRealtimeUs) {
         if (t != null) {
             boolean ret = true;
             for (int i = 0; i < t.length; i++) {
-                ret &= resetIfNotNull(t[i], detachIfReset);
+                ret &= resetIfNotNull(t[i], detachIfReset, elapsedRealtimeUs);
             }
             return ret;
         }
@@ -6627,9 +7104,9 @@
     }
 
     private static boolean resetIfNotNull(ControllerActivityCounterImpl counter,
-            boolean detachIfReset) {
+            boolean detachIfReset, long elapsedRealtimeUs) {
         if (counter != null) {
-            counter.reset(detachIfReset);
+            counter.reset(detachIfReset, elapsedRealtimeUs);
         }
         return true;
     }
@@ -6812,10 +7289,10 @@
         /**
          * The CPU times we had at the last history details update.
          */
-        long mLastStepUserTime;
-        long mLastStepSystemTime;
-        long mCurStepUserTime;
-        long mCurStepSystemTime;
+        long mLastStepUserTimeMs;
+        long mLastStepSystemTimeMs;
+        long mCurStepUserTimeMs;
+        long mCurStepSystemTimeMs;
 
         LongSamplingCounter mUserCpuTime;
         LongSamplingCounter mSystemCpuTime;
@@ -6915,17 +7392,19 @@
         private double mProportionalSystemServiceUsage;
 
         public Uid(BatteryStatsImpl bsi, int uid) {
+            this(bsi, uid, bsi.mClocks.elapsedRealtime(), bsi.mClocks.uptimeMillis());
+        }
+
+        public Uid(BatteryStatsImpl bsi, int uid, long elapsedRealtimeMs, long uptimeMs) {
             mBsi = bsi;
             mUid = uid;
 
             /* Observer list of TimeBase object in Uid is short */
             mOnBatteryBackgroundTimeBase = new TimeBase(false);
-            mOnBatteryBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000,
-                    mBsi.mClocks.elapsedRealtime() * 1000);
+            mOnBatteryBackgroundTimeBase.init(uptimeMs * 1000, elapsedRealtimeMs * 1000);
             /* Observer list of TimeBase object in Uid is short */
             mOnBatteryScreenOffBackgroundTimeBase = new TimeBase(false);
-            mOnBatteryScreenOffBackgroundTimeBase.init(mBsi.mClocks.uptimeMillis() * 1000,
-                    mBsi.mClocks.elapsedRealtime() * 1000);
+            mOnBatteryScreenOffBackgroundTimeBase.init(uptimeMs * 1000, elapsedRealtimeMs * 1000);
 
             mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
             mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
@@ -7520,13 +7999,13 @@
             return mVibratorOnTimer;
         }
 
-        public void noteVibratorOnLocked(long durationMillis) {
-            createVibratorOnTimerLocked().addDuration(mBsi, durationMillis);
+        public void noteVibratorOnLocked(long durationMillis, long elapsedRealtimeMs) {
+            createVibratorOnTimerLocked().addDuration(mBsi, durationMillis, elapsedRealtimeMs);
         }
 
-        public void noteVibratorOffLocked() {
+        public void noteVibratorOffLocked(long elapsedRealtimeMs) {
             if (mVibratorOnTimer != null) {
-                mVibratorOnTimer.abortLastDuration(mBsi);
+                mVibratorOnTimer.abortLastDuration(mBsi, elapsedRealtimeMs);
             }
         }
 
@@ -7962,58 +8441,58 @@
          * inactive so can be dropped.
          */
         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public boolean reset(long uptime, long realtime) {
+        public boolean reset(long uptimeUs, long realtimeUs) {
             boolean active = false;
 
-            mOnBatteryBackgroundTimeBase.init(uptime, realtime);
-            mOnBatteryScreenOffBackgroundTimeBase.init(uptime, realtime);
+            mOnBatteryBackgroundTimeBase.init(uptimeUs, realtimeUs);
+            mOnBatteryScreenOffBackgroundTimeBase.init(uptimeUs, realtimeUs);
 
             if (mWifiRunningTimer != null) {
-                active |= !mWifiRunningTimer.reset(false);
+                active |= !mWifiRunningTimer.reset(false, realtimeUs);
                 active |= mWifiRunning;
             }
             if (mFullWifiLockTimer != null) {
-                active |= !mFullWifiLockTimer.reset(false);
+                active |= !mFullWifiLockTimer.reset(false, realtimeUs);
                 active |= mFullWifiLockOut;
             }
             if (mWifiScanTimer != null) {
-                active |= !mWifiScanTimer.reset(false);
+                active |= !mWifiScanTimer.reset(false, realtimeUs);
                 active |= mWifiScanStarted;
             }
             if (mWifiBatchedScanTimer != null) {
                 for (int i = 0; i < NUM_WIFI_BATCHED_SCAN_BINS; i++) {
                     if (mWifiBatchedScanTimer[i] != null) {
-                        active |= !mWifiBatchedScanTimer[i].reset(false);
+                        active |= !mWifiBatchedScanTimer[i].reset(false, realtimeUs);
                     }
                 }
                 active |= (mWifiBatchedScanBinStarted != NO_BATCHED_SCAN_STARTED);
             }
             if (mWifiMulticastTimer != null) {
-                active |= !mWifiMulticastTimer.reset(false);
+                active |= !mWifiMulticastTimer.reset(false, realtimeUs);
                 active |= (mWifiMulticastWakelockCount > 0);
             }
 
-            active |= !resetIfNotNull(mAudioTurnedOnTimer, false);
-            active |= !resetIfNotNull(mVideoTurnedOnTimer, false);
-            active |= !resetIfNotNull(mFlashlightTurnedOnTimer, false);
-            active |= !resetIfNotNull(mCameraTurnedOnTimer, false);
-            active |= !resetIfNotNull(mForegroundActivityTimer, false);
-            active |= !resetIfNotNull(mForegroundServiceTimer, false);
-            active |= !resetIfNotNull(mAggregatedPartialWakelockTimer, false);
-            active |= !resetIfNotNull(mBluetoothScanTimer, false);
-            active |= !resetIfNotNull(mBluetoothUnoptimizedScanTimer, false);
+            active |= !resetIfNotNull(mAudioTurnedOnTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mVideoTurnedOnTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mFlashlightTurnedOnTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mCameraTurnedOnTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mForegroundActivityTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mForegroundServiceTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mAggregatedPartialWakelockTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mBluetoothScanTimer, false, realtimeUs);
+            active |= !resetIfNotNull(mBluetoothUnoptimizedScanTimer, false, realtimeUs);
 
-            resetIfNotNull(mBluetoothScanResultCounter, false);
-            resetIfNotNull(mBluetoothScanResultBgCounter, false);
+            resetIfNotNull(mBluetoothScanResultCounter, false, realtimeUs);
+            resetIfNotNull(mBluetoothScanResultBgCounter, false, realtimeUs);
 
             if (mProcessStateTimer != null) {
                 for (int i = 0; i < NUM_PROCESS_STATE; i++) {
-                    active |= !resetIfNotNull(mProcessStateTimer[i], false);
+                    active |= !resetIfNotNull(mProcessStateTimer[i], false, realtimeUs);
                 }
                 active |= (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT);
             }
             if (mVibratorOnTimer != null) {
-                if (mVibratorOnTimer.reset(false)) {
+                if (mVibratorOnTimer.reset(false, realtimeUs)) {
                     mVibratorOnTimer.detach();
                     mVibratorOnTimer = null;
                 } else {
@@ -8021,80 +8500,81 @@
                 }
             }
 
-            resetIfNotNull(mUserActivityCounters, false);
+            resetIfNotNull(mUserActivityCounters, false, realtimeUs);
 
-            resetIfNotNull(mNetworkByteActivityCounters, false);
-            resetIfNotNull(mNetworkPacketActivityCounters, false);
-            resetIfNotNull(mMobileRadioActiveTime, false);
-            resetIfNotNull(mMobileRadioActiveCount, false);
+            resetIfNotNull(mNetworkByteActivityCounters, false, realtimeUs);
+            resetIfNotNull(mNetworkPacketActivityCounters, false, realtimeUs);
+            resetIfNotNull(mMobileRadioActiveTime, false, realtimeUs);
+            resetIfNotNull(mMobileRadioActiveCount, false, realtimeUs);
 
-            resetIfNotNull(mWifiControllerActivity, false);
-            resetIfNotNull(mBluetoothControllerActivity, false);
-            resetIfNotNull(mModemControllerActivity, false);
+            resetIfNotNull(mWifiControllerActivity, false, realtimeUs);
+            resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
+            resetIfNotNull(mModemControllerActivity, false, realtimeUs);
 
-            resetIfNotNull(mUserCpuTime, false);
-            resetIfNotNull(mSystemCpuTime, false);
+            resetIfNotNull(mUserCpuTime, false, realtimeUs);
+            resetIfNotNull(mSystemCpuTime, false, realtimeUs);
 
-            resetIfNotNull(mCpuClusterSpeedTimesUs, false);
+            resetIfNotNull(mCpuClusterSpeedTimesUs, false, realtimeUs);
 
-            resetIfNotNull(mCpuFreqTimeMs, false);
-            resetIfNotNull(mScreenOffCpuFreqTimeMs, false);
+            resetIfNotNull(mCpuFreqTimeMs, false, realtimeUs /* unused */);
+            resetIfNotNull(mScreenOffCpuFreqTimeMs, false, realtimeUs /* unused */);
 
 
-            resetIfNotNull(mCpuActiveTimeMs, false);
-            resetIfNotNull(mCpuClusterTimesMs, false);
+            resetIfNotNull(mCpuActiveTimeMs, false, realtimeUs /* unused */);
+            resetIfNotNull(mCpuClusterTimesMs, false, realtimeUs /* unused */);
 
-            resetIfNotNull(mProcStateTimeMs, false);
+            resetIfNotNull(mProcStateTimeMs, false, realtimeUs /* unused */);
 
-            resetIfNotNull(mProcStateScreenOffTimeMs, false);
+            resetIfNotNull(mProcStateScreenOffTimeMs, false, realtimeUs /* unused */);
 
-            resetIfNotNull(mMobileRadioApWakeupCount, false);
+            resetIfNotNull(mMobileRadioApWakeupCount, false, realtimeUs);
 
-            resetIfNotNull(mWifiRadioApWakeupCount, false);
+            resetIfNotNull(mWifiRadioApWakeupCount, false, realtimeUs);
 
 
             final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
             for (int iw=wakeStats.size()-1; iw>=0; iw--) {
                 Wakelock wl = wakeStats.valueAt(iw);
-                if (wl.reset()) {
+                if (wl.reset(realtimeUs)) {
                     wakeStats.removeAt(iw);
                 } else {
                     active = true;
                 }
             }
-            mWakelockStats.cleanup();
+            final long realtimeMs = realtimeUs / 1000;
+            mWakelockStats.cleanup(realtimeMs);
             final ArrayMap<String, DualTimer> syncStats = mSyncStats.getMap();
             for (int is=syncStats.size()-1; is>=0; is--) {
                 DualTimer timer = syncStats.valueAt(is);
-                if (timer.reset(false)) {
+                if (timer.reset(false, realtimeUs)) {
                     syncStats.removeAt(is);
                     timer.detach();
                 } else {
                     active = true;
                 }
             }
-            mSyncStats.cleanup();
+            mSyncStats.cleanup(realtimeMs);
             final ArrayMap<String, DualTimer> jobStats = mJobStats.getMap();
             for (int ij=jobStats.size()-1; ij>=0; ij--) {
                 DualTimer timer = jobStats.valueAt(ij);
-                if (timer.reset(false)) {
+                if (timer.reset(false, realtimeUs)) {
                     jobStats.removeAt(ij);
                     timer.detach();
                 } else {
                     active = true;
                 }
             }
-            mJobStats.cleanup();
+            mJobStats.cleanup(realtimeMs);
             mJobCompletions.clear();
 
-            resetIfNotNull(mJobsDeferredEventCount, false);
-            resetIfNotNull(mJobsDeferredCount, false);
-            resetIfNotNull(mJobsFreshnessTimeMs, false);
-            resetIfNotNull(mJobsFreshnessBuckets, false);
+            resetIfNotNull(mJobsDeferredEventCount, false, realtimeUs);
+            resetIfNotNull(mJobsDeferredCount, false, realtimeUs);
+            resetIfNotNull(mJobsFreshnessTimeMs, false, realtimeUs /* unused */);
+            resetIfNotNull(mJobsFreshnessBuckets, false, realtimeUs);
 
             for (int ise = mSensorStats.size() - 1; ise >= 0; ise--) {
                 Sensor s = mSensorStats.valueAt(ise);
-                if (s.reset()) {
+                if (s.reset(realtimeUs)) {
                     mSensorStats.removeAt(ise);
                 } else {
                     active = true;
@@ -8128,8 +8608,8 @@
 
             mProportionalSystemServiceUsage = 0;
 
-            mLastStepUserTime = mLastStepSystemTime = 0;
-            mCurStepUserTime = mCurStepSystemTime = 0;
+            mLastStepUserTimeMs = mLastStepSystemTimeMs = 0;
+            mCurStepUserTimeMs = mCurStepSystemTimeMs = 0;
 
 
             return !active;
@@ -8943,13 +9423,13 @@
                 return new DualTimer(mBsi.mClocks, mUid, type, pool, timeBase, bgTimeBase, in);
             }
 
-            boolean reset() {
+            boolean reset(long elapsedRealtimeUs) {
                 boolean wlactive = false;
 
-                wlactive |= !resetIfNotNull(mTimerFull,false);
-                wlactive |= !resetIfNotNull(mTimerPartial,false);
-                wlactive |= !resetIfNotNull(mTimerWindow,false);
-                wlactive |= !resetIfNotNull(mTimerDraw,false);
+                wlactive |= !resetIfNotNull(mTimerFull, false, elapsedRealtimeUs);
+                wlactive |= !resetIfNotNull(mTimerPartial, false, elapsedRealtimeUs);
+                wlactive |= !resetIfNotNull(mTimerWindow, false, elapsedRealtimeUs);
+                wlactive |= !resetIfNotNull(mTimerDraw, false, elapsedRealtimeUs);
 
                 if (!wlactive) {
                     detachIfNotNull(mTimerFull);
@@ -9040,8 +9520,8 @@
                 return new DualTimer(mBsi.mClocks, mUid, 0, pool, timeBase, bgTimeBase, in);
             }
 
-            boolean reset() {
-                if (mTimer.reset(true)) {
+            boolean reset(long elapsedRealtimeUs) {
+                if (mTimer.reset(true, elapsedRealtimeUs)) {
                     mTimer = null;
                     return true;
                 }
@@ -9103,17 +9583,17 @@
             /**
              * Total time (in ms) spent executing in user code.
              */
-            long mUserTime;
+            long mUserTimeMs;
 
             /**
              * Total time (in ms) spent executing in kernel code.
              */
-            long mSystemTime;
+            long mSystemTimeMs;
 
             /**
              * Amount of time (in ms) the process was running in the foreground.
              */
-            long mForegroundTime;
+            long mForegroundTimeMs;
 
             /**
              * Number of times the process has been started.
@@ -9138,14 +9618,16 @@
                 mBsi.mOnBatteryTimeBase.add(this);
             }
 
-            public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
+            public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs,
+                    long baseRealtimeUs) {
             }
 
-            public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+            public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs,
+                    long baseRealtimeUs) {
             }
 
             @Override
-            public boolean reset(boolean detachIfReset) {
+            public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
                 if (detachIfReset) {
                     this.detach();
                 }
@@ -9169,14 +9651,14 @@
                 return null;
             }
 
-            public void addExcessiveCpu(long overTime, long usedTime) {
+            public void addExcessiveCpu(long overTimeMs, long usedTimeMs) {
                 if (mExcessivePower == null) {
                     mExcessivePower = new ArrayList<ExcessivePower>();
                 }
                 ExcessivePower ew = new ExcessivePower();
                 ew.type = ExcessivePower.TYPE_CPU;
-                ew.overTime = overTime;
-                ew.usedTime = usedTime;
+                ew.overTime = overTimeMs;
+                ew.usedTime = usedTimeMs;
                 mExcessivePower.add(ew);
             }
 
@@ -9219,9 +9701,9 @@
             }
 
             void writeToParcelLocked(Parcel out) {
-                out.writeLong(mUserTime);
-                out.writeLong(mSystemTime);
-                out.writeLong(mForegroundTime);
+                out.writeLong(mUserTimeMs);
+                out.writeLong(mSystemTimeMs);
+                out.writeLong(mForegroundTimeMs);
                 out.writeInt(mStarts);
                 out.writeInt(mNumCrashes);
                 out.writeInt(mNumAnrs);
@@ -9229,9 +9711,9 @@
             }
 
             void readFromParcelLocked(Parcel in) {
-                mUserTime = in.readLong();
-                mSystemTime = in.readLong();
-                mForegroundTime = in.readLong();
+                mUserTimeMs = in.readLong();
+                mSystemTimeMs = in.readLong();
+                mForegroundTimeMs = in.readLong();
                 mStarts = in.readInt();
                 mNumCrashes = in.readInt();
                 mNumAnrs = in.readInt();
@@ -9239,20 +9721,20 @@
             }
 
             @UnsupportedAppUsage
-            public void addCpuTimeLocked(int utime, int stime) {
-                addCpuTimeLocked(utime, stime, mBsi.mOnBatteryTimeBase.isRunning());
+            public void addCpuTimeLocked(int utimeMs, int stimeMs) {
+                addCpuTimeLocked(utimeMs, stimeMs, mBsi.mOnBatteryTimeBase.isRunning());
             }
 
-            public void addCpuTimeLocked(int utime, int stime, boolean isRunning) {
+            public void addCpuTimeLocked(int utimeMs, int stimeMs, boolean isRunning) {
                 if (isRunning) {
-                    mUserTime += utime;
-                    mSystemTime += stime;
+                    mUserTimeMs += utimeMs;
+                    mSystemTimeMs += stimeMs;
                 }
             }
 
             @UnsupportedAppUsage
-            public void addForegroundTimeLocked(long ttime) {
-                mForegroundTime += ttime;
+            public void addForegroundTimeLocked(long ttimeMs) {
+                mForegroundTimeMs += ttimeMs;
             }
 
             @UnsupportedAppUsage
@@ -9276,19 +9758,19 @@
             @Override
             @UnsupportedAppUsage
             public long getUserTime(int which) {
-                return mUserTime;
+                return mUserTimeMs;
             }
 
             @Override
             @UnsupportedAppUsage
             public long getSystemTime(int which) {
-                return mSystemTime;
+                return mSystemTimeMs;
             }
 
             @Override
             @UnsupportedAppUsage
             public long getForegroundTime(int which) {
-                return mForegroundTime;
+                return mForegroundTimeMs;
             }
 
             @Override
@@ -9333,14 +9815,16 @@
                 mBsi.mOnBatteryScreenOffTimeBase.add(this);
             }
 
-            public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
+            public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs,
+                    long baseRealtimeUs) {
             }
 
-            public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
+            public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs,
+                    long baseRealtimeUs) {
             }
 
             @Override
-            public boolean reset(boolean detachIfReset) {
+            public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
                 if (detachIfReset) {
                     this.detach();
                 }
@@ -9430,13 +9914,13 @@
                 /**
                  * Total time (ms in battery uptime) the service has been left started.
                  */
-                protected long mStartTime;
+                protected long mStartTimeMs;
 
                 /**
                  * If service has been started and not yet stopped, this is
                  * when it was started.
                  */
-                protected long mRunningSince;
+                protected long mRunningSinceMs;
 
                 /**
                  * True if we are currently running.
@@ -9451,13 +9935,13 @@
                 /**
                  * Total time (ms in battery uptime) the service has been left launched.
                  */
-                protected long mLaunchedTime;
+                protected long mLaunchedTimeMs;
 
                 /**
                  * If service has been launched and not yet exited, this is
                  * when it was launched (ms in battery uptime).
                  */
-                protected long mLaunchedSince;
+                protected long mLaunchedSinceMs;
 
                 /**
                  * True if we are currently launched.
@@ -9477,16 +9961,16 @@
                     mBsi.mOnBatteryTimeBase.add(this);
                 }
 
-                public void onTimeStarted(long elapsedRealtime, long baseUptime,
-                        long baseRealtime) {
+                public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs,
+                        long baseRealtimeUs) {
                 }
 
-                public void onTimeStopped(long elapsedRealtime, long baseUptime,
-                        long baseRealtime) {
+                public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs,
+                        long baseRealtimeUs) {
                 }
 
                 @Override
-                public boolean reset(boolean detachIfReset) {
+                public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
                     if (detachIfReset) {
                         this.detach();
                     }
@@ -9495,59 +9979,68 @@
 
                 /**
                  * Remove this Serv as a listener from the time base.
-                 */
+                 Ms*/
                 @Override
                 public void detach() {
                     mBsi.mOnBatteryTimeBase.remove(this);
                 }
 
                 public void readFromParcelLocked(Parcel in) {
-                    mStartTime = in.readLong();
-                    mRunningSince = in.readLong();
+                    mStartTimeMs = in.readLong();
+                    mRunningSinceMs = in.readLong();
                     mRunning = in.readInt() != 0;
                     mStarts = in.readInt();
-                    mLaunchedTime = in.readLong();
-                    mLaunchedSince = in.readLong();
+                    mLaunchedTimeMs = in.readLong();
+                    mLaunchedSinceMs = in.readLong();
                     mLaunched = in.readInt() != 0;
                     mLaunches = in.readInt();
                 }
 
                 public void writeToParcelLocked(Parcel out) {
-                    out.writeLong(mStartTime);
-                    out.writeLong(mRunningSince);
+                    out.writeLong(mStartTimeMs);
+                    out.writeLong(mRunningSinceMs);
                     out.writeInt(mRunning ? 1 : 0);
                     out.writeInt(mStarts);
-                    out.writeLong(mLaunchedTime);
-                    out.writeLong(mLaunchedSince);
+                    out.writeLong(mLaunchedTimeMs);
+                    out.writeLong(mLaunchedSinceMs);
                     out.writeInt(mLaunched ? 1 : 0);
                     out.writeInt(mLaunches);
                 }
 
-                public long getLaunchTimeToNowLocked(long batteryUptime) {
-                    if (!mLaunched) return mLaunchedTime;
-                    return mLaunchedTime + batteryUptime - mLaunchedSince;
+                public long getLaunchTimeToNowLocked(long batteryUptimeMs) {
+                    if (!mLaunched) return mLaunchedTimeMs;
+                    return mLaunchedTimeMs + batteryUptimeMs - mLaunchedSinceMs;
                 }
 
-                public long getStartTimeToNowLocked(long batteryUptime) {
-                    if (!mRunning) return mStartTime;
-                    return mStartTime + batteryUptime - mRunningSince;
+                public long getStartTimeToNowLocked(long batteryUptimeMs) {
+                    if (!mRunning) return mStartTimeMs;
+                    return mStartTimeMs + batteryUptimeMs - mRunningSinceMs;
                 }
 
                 @UnsupportedAppUsage
                 public void startLaunchedLocked() {
+                    startLaunchedLocked(mBsi.mClocks.uptimeMillis());
+                }
+
+                public void startLaunchedLocked(long uptimeMs) {
                     if (!mLaunched) {
                         mLaunches++;
-                        mLaunchedSince = mBsi.getBatteryUptimeLocked();
+                        mLaunchedSinceMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000;
                         mLaunched = true;
                     }
                 }
 
                 @UnsupportedAppUsage
                 public void stopLaunchedLocked() {
+                    stopLaunchedLocked(mBsi.mClocks.uptimeMillis());
+                }
+
+                public void stopLaunchedLocked(long uptimeMs) {
                     if (mLaunched) {
-                        long time = mBsi.getBatteryUptimeLocked() - mLaunchedSince;
-                        if (time > 0) {
-                            mLaunchedTime += time;
+                        long timeMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000
+                                - mLaunchedSinceMs;
+                        if (timeMs > 0) {
+                            mLaunchedTimeMs += timeMs;
                         } else {
                             mLaunches--;
                         }
@@ -9557,19 +10050,28 @@
 
                 @UnsupportedAppUsage
                 public void startRunningLocked() {
+                    startRunningLocked(mBsi.mClocks.uptimeMillis());
+                }
+
+                public void startRunningLocked(long uptimeMs) {
                     if (!mRunning) {
                         mStarts++;
-                        mRunningSince = mBsi.getBatteryUptimeLocked();
+                        mRunningSinceMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000;
                         mRunning = true;
                     }
                 }
 
                 @UnsupportedAppUsage
                 public void stopRunningLocked() {
+                    stopRunningLocked(mBsi.mClocks.uptimeMillis());
+                }
+
+                public void stopRunningLocked(long uptimeMs) {
                     if (mRunning) {
-                        long time = mBsi.getBatteryUptimeLocked() - mRunningSince;
-                        if (time > 0) {
-                            mStartTime += time;
+                        long timeMs = mBsi.getBatteryUptimeLocked(uptimeMs) / 1000
+                                - mRunningSinceMs;
+                        if (timeMs > 0) {
+                            mStartTimeMs += timeMs;
                         } else {
                             mStarts--;
                         }
@@ -9619,6 +10121,12 @@
 
         @GuardedBy("mBsi")
         public void updateUidProcessStateLocked(int procState) {
+            updateUidProcessStateLocked(procState,
+                    mBsi.mClocks.elapsedRealtime(), mBsi.mClocks.uptimeMillis());
+        }
+
+        public void updateUidProcessStateLocked(int procState,
+                long elapsedRealtimeMs, long uptimeMs) {
             int uidRunningState;
             // Make special note of Foreground Services
             final boolean userAwareService =
@@ -9629,10 +10137,7 @@
                 return;
             }
 
-            final long elapsedRealtimeMs = mBsi.mClocks.elapsedRealtime();
             if (mProcessState != uidRunningState) {
-                final long uptimeMs = mBsi.mClocks.uptimeMillis();
-
                 if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
                     mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
 
@@ -9789,28 +10294,28 @@
         }
 
         public void noteStartSyncLocked(String name, long elapsedRealtimeMs) {
-            DualTimer t = mSyncStats.startObject(name);
+            DualTimer t = mSyncStats.startObject(name, elapsedRealtimeMs);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
             }
         }
 
         public void noteStopSyncLocked(String name, long elapsedRealtimeMs) {
-            DualTimer t = mSyncStats.stopObject(name);
+            DualTimer t = mSyncStats.stopObject(name, elapsedRealtimeMs);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
             }
         }
 
         public void noteStartJobLocked(String name, long elapsedRealtimeMs) {
-            DualTimer t = mJobStats.startObject(name);
+            DualTimer t = mJobStats.startObject(name, elapsedRealtimeMs);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
             }
         }
 
         public void noteStopJobLocked(String name, long elapsedRealtimeMs, int stopReason) {
-            DualTimer t = mJobStats.stopObject(name);
+            DualTimer t = mJobStats.stopObject(name, elapsedRealtimeMs);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
             }
@@ -9873,7 +10378,7 @@
         }
 
         public void noteStartWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
-            Wakelock wl = mWakelockStats.startObject(name);
+            Wakelock wl = mWakelockStats.startObject(name, elapsedRealtimeMs);
             if (wl != null) {
                 getWakelockTimerLocked(wl, type).startRunningLocked(elapsedRealtimeMs);
             }
@@ -9889,7 +10394,7 @@
         }
 
         public void noteStopWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
-            Wakelock wl = mWakelockStats.stopObject(name);
+            Wakelock wl = mWakelockStats.stopObject(name, elapsedRealtimeMs);
             if (wl != null) {
                 StopwatchTimer wlt = getWakelockTimerLocked(wl, type);
                 wlt.stopRunningLocked(elapsedRealtimeMs);
@@ -9910,10 +10415,10 @@
             }
         }
 
-        public void reportExcessiveCpuLocked(String proc, long overTime, long usedTime) {
+        public void reportExcessiveCpuLocked(String proc, long overTimeMs, long usedTimeMs) {
             Proc p = getProcessStatsLocked(proc);
             if (p != null) {
-                p.addExcessiveCpu(overTime, usedTime);
+                p.addExcessiveCpu(overTimeMs, usedTimeMs);
             }
         }
 
@@ -10041,16 +10546,16 @@
         mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
         mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
         mOnBattery = mOnBatteryInternal = false;
-        long uptime = mClocks.uptimeMillis() * 1000;
-        long realtime = mClocks.elapsedRealtime() * 1000;
-        initTimes(uptime, realtime);
+        long uptimeUs = mClocks.uptimeMillis() * 1000;
+        long realtimeUs = mClocks.elapsedRealtime() * 1000;
+        initTimes(uptimeUs, realtimeUs);
         mStartPlatformVersion = mEndPlatformVersion = Build.ID;
         mDischargeStartLevel = 0;
         mDischargeUnplugLevel = 0;
         mDischargePlugLevel = -1;
         mDischargeCurrentLevel = 0;
         mCurrentBatteryLevel = 0;
-        initDischarge();
+        initDischarge(realtimeUs);
         clearHistoryLocked();
         updateDailyDeadlineLocked();
         mPlatformIdleStateCallback = cb;
@@ -10110,9 +10615,9 @@
         mCallback = cb;
     }
 
-    public void setRadioScanningTimeoutLocked(long timeout) {
+    public void setRadioScanningTimeoutLocked(long timeoutUs) {
         if (mPhoneSignalScanningTimer != null) {
-            mPhoneSignalScanningTimer.setTimeout(timeout);
+            mPhoneSignalScanningTimer.setTimeout(timeoutUs);
         }
     }
 
@@ -10122,9 +10627,9 @@
 
     public void updateDailyDeadlineLocked() {
         // Get the current time.
-        long currentTime = mDailyStartTime = System.currentTimeMillis();
+        long currentTimeMs = mDailyStartTimeMs = System.currentTimeMillis();
         Calendar calDeadline = Calendar.getInstance();
-        calDeadline.setTimeInMillis(currentTime);
+        calDeadline.setTimeInMillis(currentTimeMs);
 
         // Move time up to the next day, ranging from 1am to 3pm.
         calDeadline.set(Calendar.DAY_OF_YEAR, calDeadline.get(Calendar.DAY_OF_YEAR) + 1);
@@ -10132,25 +10637,24 @@
         calDeadline.set(Calendar.SECOND, 0);
         calDeadline.set(Calendar.MINUTE, 0);
         calDeadline.set(Calendar.HOUR_OF_DAY, 1);
-        mNextMinDailyDeadline = calDeadline.getTimeInMillis();
+        mNextMinDailyDeadlineMs = calDeadline.getTimeInMillis();
         calDeadline.set(Calendar.HOUR_OF_DAY, 3);
-        mNextMaxDailyDeadline = calDeadline.getTimeInMillis();
+        mNextMaxDailyDeadlineMs = calDeadline.getTimeInMillis();
     }
 
-    public void recordDailyStatsIfNeededLocked(boolean settled) {
-        long currentTime = System.currentTimeMillis();
-        if (currentTime >= mNextMaxDailyDeadline) {
+    public void recordDailyStatsIfNeededLocked(boolean settled, long currentTimeMs) {
+        if (currentTimeMs >= mNextMaxDailyDeadlineMs) {
             recordDailyStatsLocked();
-        } else if (settled && currentTime >= mNextMinDailyDeadline) {
+        } else if (settled && currentTimeMs >= mNextMinDailyDeadlineMs) {
             recordDailyStatsLocked();
-        } else if (currentTime < (mDailyStartTime-(1000*60*60*24))) {
+        } else if (currentTimeMs < (mDailyStartTimeMs - (1000 * 60 * 60 * 24))) {
             recordDailyStatsLocked();
         }
     }
 
     public void recordDailyStatsLocked() {
         DailyItem item = new DailyItem();
-        item.mStartTime = mDailyStartTime;
+        item.mStartTime = mDailyStartTimeMs;
         item.mEndTime = System.currentTimeMillis();
         boolean hasData = false;
         if (mDailyDischargeStepTracker.mNumStepDurations > 0) {
@@ -10175,7 +10679,7 @@
         updateDailyDeadlineLocked();
 
         if (hasData) {
-            final long startTime = SystemClock.uptimeMillis();
+            final long startTimeMs = SystemClock.uptimeMillis();
             mDailyItems.add(item);
             while (mDailyItems.size() > MAX_DAILY_ITEMS) {
                 mDailyItems.remove(0);
@@ -10185,12 +10689,12 @@
                 XmlSerializer out = new FastXmlSerializer();
                 out.setOutput(memStream, StandardCharsets.UTF_8.name());
                 writeDailyItemsLocked(out);
-                final long initialTime = SystemClock.uptimeMillis() - startTime;
+                final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs;
                 BackgroundThread.getHandler().post(new Runnable() {
                     @Override
                     public void run() {
                         synchronized (mCheckinFile) {
-                            final long startTime2 = SystemClock.uptimeMillis();
+                            final long startTimeMs2 = SystemClock.uptimeMillis();
                             FileOutputStream stream = null;
                             try {
                                 stream = mDailyFile.startWrite();
@@ -10199,7 +10703,7 @@
                                 mDailyFile.finishWrite(stream);
                                 com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
                                         "batterystats-daily",
-                                        initialTime + SystemClock.uptimeMillis() - startTime2);
+                                        initialTimeMs + SystemClock.uptimeMillis() - startTimeMs2);
                             } catch (IOException e) {
                                 Slog.w("BatteryStats",
                                         "Error writing battery daily items", e);
@@ -10431,17 +10935,17 @@
 
     @Override
     public long getCurrentDailyStartTime() {
-        return mDailyStartTime;
+        return mDailyStartTimeMs;
     }
 
     @Override
     public long getNextMinDailyDeadline() {
-        return mNextMinDailyDeadline;
+        return mNextMinDailyDeadlineMs;
     }
 
     @Override
     public long getNextMaxDailyDeadline() {
-        return mNextMaxDailyDeadline;
+        return mNextMaxDailyDeadlineMs;
     }
 
     @Override
@@ -10554,12 +11058,12 @@
         if (p == null) {
             return false;
         }
-        final long lastRealtime = out.time;
-        final long lastWalltime = out.currentTime;
+        final long lastRealtimeMs = out.time;
+        final long lastWalltimeMs = out.currentTime;
         readHistoryDelta(p, out);
         if (out.cmd != HistoryItem.CMD_CURRENT_TIME
-                && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
-            out.currentTime = lastWalltime + (out.time - lastRealtime);
+                && out.cmd != HistoryItem.CMD_RESET && lastWalltimeMs != 0) {
+            out.currentTime = lastWalltimeMs + (out.time - lastRealtimeMs);
         }
         return true;
     }
@@ -10574,7 +11078,7 @@
 
     @Override
     public long getHistoryBaseTime() {
-        return mHistoryBaseTime;
+        return mHistoryBaseTimeMs;
     }
 
     @Override
@@ -10604,17 +11108,17 @@
         return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND;
     }
 
-    void initTimes(long uptime, long realtime) {
-        mStartClockTime = System.currentTimeMillis();
-        mOnBatteryTimeBase.init(uptime, realtime);
-        mOnBatteryScreenOffTimeBase.init(uptime, realtime);
-        mRealtime = 0;
-        mUptime = 0;
-        mRealtimeStart = realtime;
-        mUptimeStart = uptime;
+    void initTimes(long uptimeUs, long realtimeUs) {
+        mStartClockTimeMs = System.currentTimeMillis();
+        mOnBatteryTimeBase.init(uptimeUs, realtimeUs);
+        mOnBatteryScreenOffTimeBase.init(uptimeUs, realtimeUs);
+        mRealtimeUs = 0;
+        mUptimeUs = 0;
+        mRealtimeStartUs = realtimeUs;
+        mUptimeStartUs = uptimeUs;
     }
 
-    void initDischarge() {
+    void initDischarge(long elapsedRealtimeUs) {
         mLowDischargeAmountSinceCharge = 0;
         mHighDischargeAmountSinceCharge = 0;
         mDischargeAmountScreenOn = 0;
@@ -10625,26 +11129,26 @@
         mDischargeAmountScreenDozeSinceCharge = 0;
         mDischargeStepTracker.init();
         mChargeStepTracker.init();
-        mDischargeScreenOffCounter.reset(false);
-        mDischargeScreenDozeCounter.reset(false);
-        mDischargeLightDozeCounter.reset(false);
-        mDischargeDeepDozeCounter.reset(false);
-        mDischargeCounter.reset(false);
+        mDischargeScreenOffCounter.reset(false, elapsedRealtimeUs);
+        mDischargeScreenDozeCounter.reset(false, elapsedRealtimeUs);
+        mDischargeLightDozeCounter.reset(false, elapsedRealtimeUs);
+        mDischargeDeepDozeCounter.reset(false, elapsedRealtimeUs);
+        mDischargeCounter.reset(false, elapsedRealtimeUs);
     }
 
     public void resetAllStatsCmdLocked() {
-        resetAllStatsLocked();
         final long mSecUptime = mClocks.uptimeMillis();
-        long uptime = mSecUptime * 1000;
+        long uptimeUs = mSecUptime * 1000;
         long mSecRealtime = mClocks.elapsedRealtime();
-        long realtime = mSecRealtime * 1000;
+        long realtimeUs = mSecRealtime * 1000;
+        resetAllStatsLocked(mSecUptime, mSecRealtime);
         mDischargeStartLevel = mHistoryCur.batteryLevel;
         pullPendingStateUpdatesLocked();
         addHistoryRecordLocked(mSecRealtime, mSecUptime);
         mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel
                 = mCurrentBatteryLevel = mHistoryCur.batteryLevel;
-        mOnBatteryTimeBase.reset(uptime, realtime);
-        mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
+        mOnBatteryTimeBase.reset(uptimeUs, realtimeUs);
+        mOnBatteryScreenOffTimeBase.reset(uptimeUs, realtimeUs);
         if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
             if (isScreenOn(mScreenState)) {
                 mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
@@ -10666,15 +11170,15 @@
         initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
     }
 
-    private void resetAllStatsLocked() {
-        final long uptimeMillis = mClocks.uptimeMillis();
-        final long elapsedRealtimeMillis = mClocks.elapsedRealtime();
+    private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis) {
+        final long uptimeUs = uptimeMillis * 1000;
+        final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
         mStartCount = 0;
-        initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
-        mScreenOnTimer.reset(false);
-        mScreenDozeTimer.reset(false);
+        initTimes(uptimeUs, elapsedRealtimeUs);
+        mScreenOnTimer.reset(false, uptimeUs);
+        mScreenDozeTimer.reset(false, elapsedRealtimeUs);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
-            mScreenBrightnessTimer[i].reset(false);
+            mScreenBrightnessTimer[i].reset(false, elapsedRealtimeUs);
         }
 
         if (mPowerProfile != null) {
@@ -10684,60 +11188,60 @@
         }
         mMinLearnedBatteryCapacity = -1;
         mMaxLearnedBatteryCapacity = -1;
-        mInteractiveTimer.reset(false);
-        mPowerSaveModeEnabledTimer.reset(false);
-        mLastIdleTimeStart = elapsedRealtimeMillis;
-        mLongestLightIdleTime = 0;
-        mLongestFullIdleTime = 0;
-        mDeviceIdleModeLightTimer.reset(false);
-        mDeviceIdleModeFullTimer.reset(false);
-        mDeviceLightIdlingTimer.reset(false);
-        mDeviceIdlingTimer.reset(false);
-        mPhoneOnTimer.reset(false);
-        mAudioOnTimer.reset(false);
-        mVideoOnTimer.reset(false);
-        mFlashlightOnTimer.reset(false);
-        mCameraOnTimer.reset(false);
-        mBluetoothScanTimer.reset(false);
+        mInteractiveTimer.reset(false, elapsedRealtimeUs);
+        mPowerSaveModeEnabledTimer.reset(false, elapsedRealtimeUs);
+        mLastIdleTimeStartMs = elapsedRealtimeMillis;
+        mLongestLightIdleTimeMs = 0;
+        mLongestFullIdleTimeMs = 0;
+        mDeviceIdleModeLightTimer.reset(false, elapsedRealtimeUs);
+        mDeviceIdleModeFullTimer.reset(false, elapsedRealtimeUs);
+        mDeviceLightIdlingTimer.reset(false, elapsedRealtimeUs);
+        mDeviceIdlingTimer.reset(false, elapsedRealtimeUs);
+        mPhoneOnTimer.reset(false, elapsedRealtimeUs);
+        mAudioOnTimer.reset(false, elapsedRealtimeUs);
+        mVideoOnTimer.reset(false, elapsedRealtimeUs);
+        mFlashlightOnTimer.reset(false, elapsedRealtimeUs);
+        mCameraOnTimer.reset(false, elapsedRealtimeUs);
+        mBluetoothScanTimer.reset(false, elapsedRealtimeUs);
         for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
-            mPhoneSignalStrengthsTimer[i].reset(false);
+            mPhoneSignalStrengthsTimer[i].reset(false, elapsedRealtimeUs);
         }
-        mPhoneSignalScanningTimer.reset(false);
+        mPhoneSignalScanningTimer.reset(false, elapsedRealtimeUs);
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
-            mPhoneDataConnectionsTimer[i].reset(false);
+            mPhoneDataConnectionsTimer[i].reset(false, elapsedRealtimeUs);
         }
         for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
-            mNetworkByteActivityCounters[i].reset(false);
-            mNetworkPacketActivityCounters[i].reset(false);
+            mNetworkByteActivityCounters[i].reset(false, elapsedRealtimeUs);
+            mNetworkPacketActivityCounters[i].reset(false, elapsedRealtimeUs);
         }
-        mMobileRadioActiveTimer.reset(false);
-        mMobileRadioActivePerAppTimer.reset(false);
-        mMobileRadioActiveAdjustedTime.reset(false);
-        mMobileRadioActiveUnknownTime.reset(false);
-        mMobileRadioActiveUnknownCount.reset(false);
-        mWifiOnTimer.reset(false);
-        mGlobalWifiRunningTimer.reset(false);
+        mMobileRadioActiveTimer.reset(false, elapsedRealtimeUs);
+        mMobileRadioActivePerAppTimer.reset(false, elapsedRealtimeUs);
+        mMobileRadioActiveAdjustedTime.reset(false, elapsedRealtimeUs);
+        mMobileRadioActiveUnknownTime.reset(false, elapsedRealtimeUs);
+        mMobileRadioActiveUnknownCount.reset(false, elapsedRealtimeUs);
+        mWifiOnTimer.reset(false, elapsedRealtimeUs);
+        mGlobalWifiRunningTimer.reset(false, elapsedRealtimeUs);
         for (int i=0; i<NUM_WIFI_STATES; i++) {
-            mWifiStateTimer[i].reset(false);
+            mWifiStateTimer[i].reset(false, elapsedRealtimeUs);
         }
         for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
-            mWifiSupplStateTimer[i].reset(false);
+            mWifiSupplStateTimer[i].reset(false, elapsedRealtimeUs);
         }
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
-            mWifiSignalStrengthsTimer[i].reset(false);
+            mWifiSignalStrengthsTimer[i].reset(false, elapsedRealtimeUs);
         }
-        mWifiMulticastWakelockTimer.reset(false);
-        mWifiActiveTimer.reset(false);
-        mWifiActivity.reset(false);
+        mWifiMulticastWakelockTimer.reset(false, elapsedRealtimeUs);
+        mWifiActiveTimer.reset(false, elapsedRealtimeUs);
+        mWifiActivity.reset(false, elapsedRealtimeUs);
         for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
-            mGpsSignalQualityTimer[i].reset(false);
+            mGpsSignalQualityTimer[i].reset(false, elapsedRealtimeUs);
         }
-        mBluetoothActivity.reset(false);
-        mModemActivity.reset(false);
+        mBluetoothActivity.reset(false, elapsedRealtimeUs);
+        mModemActivity.reset(false, elapsedRealtimeUs);
         mNumConnectivityChange = 0;
 
         for (int i=0; i<mUidStats.size(); i++) {
-            if (mUidStats.valueAt(i).reset(uptimeMillis * 1000, elapsedRealtimeMillis * 1000)) {
+            if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs)) {
                 mUidStats.valueAt(i).detachFromTimeBase();
                 mUidStats.remove(mUidStats.keyAt(i));
                 i--;
@@ -10780,25 +11284,25 @@
 
         mTmpRailStats.reset();
 
-        resetIfNotNull(mSystemServerThreadCpuTimesUs, false);
-        resetIfNotNull(mBinderThreadCpuTimesUs, false);
+        resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs);
+        resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
 
         mLastHistoryStepDetails = null;
-        mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
-        mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
-        mLastStepCpuUserTime = mCurStepCpuUserTime = 0;
-        mLastStepCpuSystemTime = mCurStepCpuSystemTime = 0;
-        mLastStepStatUserTime = mCurStepStatUserTime = 0;
-        mLastStepStatSystemTime = mCurStepStatSystemTime = 0;
-        mLastStepStatIOWaitTime = mCurStepStatIOWaitTime = 0;
-        mLastStepStatIrqTime = mCurStepStatIrqTime = 0;
-        mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime = 0;
-        mLastStepStatIdleTime = mCurStepStatIdleTime = 0;
+        mLastStepCpuUserTimeMs = mLastStepCpuSystemTimeMs = 0;
+        mCurStepCpuUserTimeMs = mCurStepCpuSystemTimeMs = 0;
+        mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
+        mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
+        mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
+        mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
+        mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
+        mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
+        mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
+        mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
 
         mNumAllUidCpuTimeReads = 0;
         mNumUidsRemoved = 0;
 
-        initDischarge();
+        initDischarge(elapsedRealtimeUs);
 
         clearHistoryLocked();
         mBatteryStatsHistory.resetAllFiles();
@@ -10915,6 +11419,14 @@
      * @param info The energy information from the WiFi controller.
      */
     public void updateWifiState(@Nullable final WifiActivityEnergyInfo info) {
+        updateWifiState(info, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    /**
+     * @see #updateWifiState(WifiActivityEnergyInfo)
+     */
+    public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
         }
@@ -10939,7 +11451,6 @@
                 return;
             }
 
-            final long elapsedRealtimeMs = mClocks.elapsedRealtime();
             SparseLongArray rxPackets = new SparseLongArray();
             SparseLongArray txPackets = new SparseLongArray();
             long totalTxPackets = 0;
@@ -10961,7 +11472,7 @@
                         continue;
                     }
 
-                    final Uid u = getUidStatsLocked(mapUid(entry.uid));
+                    final Uid u = getUidStatsLocked(mapUid(entry.uid), elapsedRealtimeMs, uptimeMs);
                     if (entry.rxBytes != 0) {
                         u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
                                 entry.rxPackets);
@@ -11127,7 +11638,8 @@
                 // Distribute the remaining Tx power appropriately between all apps that transmitted
                 // packets.
                 for (int i = 0; i < txPackets.size(); i++) {
-                    final Uid uid = getUidStatsLocked(txPackets.keyAt(i));
+                    final Uid uid = getUidStatsLocked(txPackets.keyAt(i),
+                            elapsedRealtimeMs, uptimeMs);
                     final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
                             / totalTxPackets;
                     if (DEBUG_ENERGY) {
@@ -11140,7 +11652,8 @@
                 // Distribute the remaining Rx power appropriately between all apps that received
                 // packets.
                 for (int i = 0; i < rxPackets.size(); i++) {
-                    final Uid uid = getUidStatsLocked(rxPackets.keyAt(i));
+                    final Uid uid = getUidStatsLocked(rxPackets.keyAt(i),
+                            elapsedRealtimeMs, uptimeMs);
                     final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
                             / totalRxPackets;
                     if (DEBUG_ENERGY) {
@@ -11179,7 +11692,7 @@
                         monitoredRailChargeConsumedMaMs);
                 mHistoryCur.wifiRailChargeMah +=
                         (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
-                addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
                 mTmpRailStats.resetWifiTotalEnergyUsed();
             }
         }
@@ -11210,13 +11723,21 @@
      * Distribute Cell radio energy info and network traffic to apps.
      */
     public void updateMobileRadioState(@Nullable final ModemActivityInfo activityInfo) {
+        updateMobileRadioState(activityInfo, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    /**
+     * @see #updateMobileRadioState(ModemActivityInfo)
+     */
+    public void updateMobileRadioState(@Nullable final ModemActivityInfo activityInfo,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
         ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
 
         // Add modem tx power to history.
-        addModemTxPowerToHistory(deltaInfo);
+        addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
 
         // Grab a separate lock to acquire the network stats, which may do I/O.
         NetworkStats delta = null;
@@ -11279,12 +11800,11 @@
                             monitoredRailChargeConsumedMaMs);
                     mHistoryCur.modemRailChargeMah +=
                             (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
-                    addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+                    addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
                     mTmpRailStats.resetCellularTotalEnergyUsed();
                 }
             }
-            final long elapsedRealtimeMs = mClocks.elapsedRealtime();
-            long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
+            long radioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000);
             mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
 
@@ -11308,7 +11828,7 @@
                     totalRxPackets += entry.rxPackets;
                     totalTxPackets += entry.txPackets;
 
-                    final Uid u = getUidStatsLocked(mapUid(entry.uid));
+                    final Uid u = getUidStatsLocked(mapUid(entry.uid), elapsedRealtimeMs, uptimeMs);
                     u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
                             entry.rxPackets);
                     u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
@@ -11339,16 +11859,17 @@
                             continue;
                         }
 
-                        final Uid u = getUidStatsLocked(mapUid(entry.uid));
+                        final Uid u = getUidStatsLocked(mapUid(entry.uid),
+                                elapsedRealtimeMs, uptimeMs);
 
                         // Distribute total radio active time in to this app.
                         final long appPackets = entry.rxPackets + entry.txPackets;
-                        final long appRadioTime = (radioTime * appPackets) / totalPackets;
-                        u.noteMobileRadioActiveTimeLocked(appRadioTime);
+                        final long appRadioTimeUs = (radioTimeUs * appPackets) / totalPackets;
+                        u.noteMobileRadioActiveTimeLocked(appRadioTimeUs);
 
                         // Remove this app from the totals, so that we don't lose any time
                         // due to rounding.
-                        radioTime -= appRadioTime;
+                        radioTimeUs -= appRadioTimeUs;
                         totalPackets -= appPackets;
 
                         if (deltaInfo != null) {
@@ -11373,9 +11894,9 @@
                     }
                 }
 
-                if (radioTime > 0) {
+                if (radioTimeUs > 0) {
                     // Whoops, there is some radio time we can't blame on an app!
-                    mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
+                    mMobileRadioActiveUnknownTime.addCountLocked(radioTimeUs);
                     mMobileRadioActiveUnknownCount.addCountLocked(1);
                 }
 
@@ -11391,7 +11912,8 @@
      * time at the highest power level.
      * @param activityInfo
      */
-    private synchronized void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+    private synchronized void addModemTxPowerToHistory(final ModemActivityInfo activityInfo,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (activityInfo == null) {
             return;
         }
@@ -11399,8 +11921,6 @@
         if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) {
             return;
         }
-        final long elapsedRealtime = mClocks.elapsedRealtime();
-        final long uptime = mClocks.uptimeMillis();
         int levelMaxTimeSpent = 0;
         for (int i = 1; i < txPowerInfo.size(); i++) {
             if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent)
@@ -11410,7 +11930,7 @@
         }
         if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
             mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
-            addHistoryRecordLocked(elapsedRealtime, uptime);
+            addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
         }
     }
 
@@ -11446,6 +11966,14 @@
      * @param info The energy information from the bluetooth controller.
      */
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
+        updateBluetoothStateLocked(info, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    /**
+     * @see #updateBluetoothStateLocked(BluetoothActivityEnergyInfo)
+     */
+    public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info,
+            long elapsedRealtimeMs, long uptimeMs) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating bluetooth stats: " + info);
         }
@@ -11456,7 +11984,6 @@
 
         mHasBluetoothReporting = true;
 
-        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
         final long rxTimeMs =
                 info.getControllerRxTimeMillis() - mLastBluetoothActivityInfo.rxTimeMs;
         final long txTimeMs =
@@ -11560,7 +12087,7 @@
             mNetworkByteActivityCounters[NETWORK_BT_TX_DATA].addCountLocked(txBytes);
 
             // Add to the UID counters.
-            final Uid u = getUidStatsLocked(mapUid(traffic.getUid()));
+            final Uid u = getUidStatsLocked(mapUid(traffic.getUid()), elapsedRealtimeMs, uptimeMs);
             u.noteNetworkActivityLocked(NETWORK_BT_RX_DATA, rxBytes, 0);
             u.noteNetworkActivityLocked(NETWORK_BT_TX_DATA, txBytes, 0);
 
@@ -11579,7 +12106,7 @@
                 final long txBytes =
                         traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(uid);
 
-                final Uid u = getUidStatsLocked(mapUid(uid));
+                final Uid u = getUidStatsLocked(mapUid(uid), elapsedRealtimeMs, uptimeMs);
                 final ControllerActivityCounterImpl counter =
                         u.getOrCreateBluetoothControllerActivityLocked();
 
@@ -11623,6 +12150,13 @@
      * instead of fetching it anew.
      */
     public void updateRpmStatsLocked() {
+        updateRpmStatsLocked(mClocks.elapsedRealtime() * 1000);
+    }
+
+    /**
+     * @see #updateRpmStatsLocked()
+     */
+    public void updateRpmStatsLocked(long elapsedRealtimeUs) {
         if (mPlatformIdleStateCallback == null) return;
         long now = SystemClock.elapsedRealtime();
         if (now - mLastRpmStatsUpdateTimeMs >= RPM_STATS_UPDATE_FREQ_MS) {
@@ -11637,9 +12171,9 @@
             final String pName = pstate.getKey();
             final long pTimeUs = pstate.getValue().mTimeMs * 1000;
             final int pCount = pstate.getValue().mCount;
-            getRpmTimerLocked(pName).update(pTimeUs, pCount);
+            getRpmTimerLocked(pName).update(pTimeUs, pCount, elapsedRealtimeUs);
             if (SCREEN_OFF_RPM_STATS_ENABLED) {
-                getScreenOffRpmTimerLocked(pName).update(pTimeUs, pCount);
+                getScreenOffRpmTimerLocked(pName).update(pTimeUs, pCount, elapsedRealtimeUs);
             }
 
             // Update values for each voter of this platform state.
@@ -11648,9 +12182,9 @@
                 final String vName = pName + "." + voter.getKey();
                 final long vTimeUs = voter.getValue().mTimeMs * 1000;
                 final int vCount = voter.getValue().mCount;
-                getRpmTimerLocked(vName).update(vTimeUs, vCount);
+                getRpmTimerLocked(vName).update(vTimeUs, vCount, elapsedRealtimeUs);
                 if (SCREEN_OFF_RPM_STATS_ENABLED) {
-                    getScreenOffRpmTimerLocked(vName).update(vTimeUs, vCount);
+                    getScreenOffRpmTimerLocked(vName).update(vTimeUs, vCount, elapsedRealtimeUs);
                 }
             }
         }
@@ -11664,9 +12198,9 @@
                 final String name = subsysName + "." + sstate.getKey();
                 final long timeUs = sstate.getValue().mTimeMs * 1000;
                 final int count = sstate.getValue().mCount;
-                getRpmTimerLocked(name).update(timeUs, count);
+                getRpmTimerLocked(name).update(timeUs, count, elapsedRealtimeUs);
                 if (SCREEN_OFF_RPM_STATS_ENABLED) {
-                    getScreenOffRpmTimerLocked(name).update(timeUs, count);
+                    getScreenOffRpmTimerLocked(name).update(timeUs, count, elapsedRealtimeUs);
                 }
             }
         }
@@ -11686,6 +12220,13 @@
      * Read and distribute kernel wake lock use across apps.
      */
     public void updateKernelWakelocksLocked() {
+        updateKernelWakelocksLocked(mClocks.elapsedRealtime() * 1000);
+    }
+
+    /**
+     * @see #updateKernelWakelocksLocked()
+     */
+    public void updateKernelWakelocksLocked(long elapsedRealtimeUs) {
         final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats(
                 mTmpWakelockStats);
         if (wakelockStats == null) {
@@ -11704,7 +12245,7 @@
                 mKernelWakelockStats.put(name, kwlt);
             }
 
-            kwlt.update(kws.mTotalTime, kws.mCount);
+            kwlt.update(kws.mTotalTime, kws.mCount, elapsedRealtimeUs);
             kwlt.setUpdateVersion(kws.mVersion);
         }
 
@@ -11714,7 +12255,7 @@
         for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
             SamplingTimer st = ent.getValue();
             if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
-                st.endSample();
+                st.endSample(elapsedRealtimeUs);
                 numWakelocksSetStale++;
             }
         }
@@ -11742,6 +12283,10 @@
      * Reads the newest memory stats from the kernel.
      */
     public void updateKernelMemoryBandwidthLocked() {
+        updateKernelMemoryBandwidthLocked(mClocks.elapsedRealtime() * 1000);
+    }
+
+    public void updateKernelMemoryBandwidthLocked(long elapsedRealtimeUs) {
         mKernelMemoryBandwidthStats.updateStats();
         LongSparseLongArray bandwidthEntries = mKernelMemoryBandwidthStats.getBandwidthEntries();
         final int bandwidthEntryCount = bandwidthEntries.size();
@@ -11754,12 +12299,12 @@
                 timer = new SamplingTimer(mClocks, mOnBatteryTimeBase);
                 mKernelMemoryStats.put(bandwidthEntries.keyAt(i), timer);
             }
-            timer.update(bandwidthEntries.valueAt(i), 1);
+            timer.update(bandwidthEntries.valueAt(i), 1, elapsedRealtimeUs);
             if (DEBUG_MEMORY) {
                 Slog.d(TAG, String.format("Added entry %d and updated timer to: "
-                        + "mUnpluggedReportedTotalTime %d size %d", bandwidthEntries.keyAt(i),
+                        + "mUnpluggedReportedTotalTimeUs %d size %d", bandwidthEntries.keyAt(i),
                         mKernelMemoryStats.get(
-                                bandwidthEntries.keyAt(i)).mUnpluggedReportedTotalTime,
+                                bandwidthEntries.keyAt(i)).mUnpluggedReportedTotalTimeUs,
                         mKernelMemoryStats.size()));
             }
         }
@@ -11898,8 +12443,8 @@
         }
         if (DEBUG_BINDER_STATS) {
             Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
-            long binderThreadTime = 0;
-            long totalThreadTime = 0;
+            long binderThreadTimeMs = 0;
+            long totalThreadTimeMs = 0;
             int cpuIndex = 0;
             for (int cluster = 0; cluster < numCpuClusters; cluster++) {
                 StringBuilder sb = new StringBuilder();
@@ -11909,26 +12454,27 @@
                     if (speed != 0) {
                         sb.append(", ");
                     }
-                    long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(
-                            0) / 1000;
-                    long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
+                    long totalCountMs =
+                            mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(0) / 1000;
+                    long binderCountMs = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
                             / 1000;
                     sb.append(String.format("%d/%d(%.1f%%)",
-                            binderCount,
-                            totalCount,
-                            totalCount != 0 ? (double) binderCount * 100 / totalCount : 0));
+                            binderCountMs,
+                            totalCountMs,
+                            totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0));
 
-                    totalThreadTime += totalCount;
-                    binderThreadTime += binderCount;
+                    totalThreadTimeMs += totalCountMs;
+                    binderThreadTimeMs += binderCountMs;
                     index++;
                 }
                 cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
                 Slog.d(TAG, sb.toString());
             }
-            Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime);
+            Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs);
             Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
-                    binderThreadTime,
-                    binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0));
+                    binderThreadTimeMs,
+                    binderThreadTimeMs != 0
+                            ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0));
         }
     }
 
@@ -11985,8 +12531,10 @@
             // So, we distribute total time spent by an uid to different cpu freqs based on the
             // amount of time cpu was running at that freq.
             final int updatedUidsCount = updatedUids.size();
+            final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+            final long uptimeMs = mClocks.uptimeMillis();
             for (int i = 0; i < updatedUidsCount; ++i) {
-                final Uid u = getUidStatsLocked(updatedUids.keyAt(i));
+                final Uid u = getUidStatsLocked(updatedUids.keyAt(i), elapsedRealtimeMs, uptimeMs);
                 final long appCpuTimeUs = updatedUids.valueAt(i);
                 // Add the cpu speeds to this UID.
                 final int numClusters = mPowerProfile.getNumCpuClusters();
@@ -12031,6 +12579,7 @@
         mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
         final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
         final long startTimeMs = mClocks.uptimeMillis();
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
 
         mCpuUidUserSysTimeReader.readDelta((uid, timesUs) -> {
             long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
@@ -12048,7 +12597,7 @@
                 mCpuUidUserSysTimeReader.removeUid(uid);
                 return;
             }
-            final Uid u = getUidStatsLocked(uid);
+            final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
 
             // Accumulate the total system and user time.
             mTempTotalCpuUserTimeUs += userTimeUs;
@@ -12144,6 +12693,7 @@
         final int numClusters = mPowerProfile.getNumCpuClusters();
         mWakeLockAllocationsUs = null;
         final long startTimeMs = mClocks.uptimeMillis();
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
         mCpuUidFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
@@ -12156,7 +12706,7 @@
                 mCpuUidFreqTimeReader.removeUid(uid);
                 return;
             }
-            final Uid u = getUidStatsLocked(uid);
+            final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
             if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
                 detachIfNotNull(u.mCpuFreqTimeMs);
                 u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
@@ -12257,6 +12807,7 @@
     @VisibleForTesting
     public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
         final long startTimeMs = mClocks.uptimeMillis();
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
         mCpuUidActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
@@ -12269,7 +12820,7 @@
                 mCpuUidActiveTimeReader.removeUid(uid);
                 return;
             }
-            final Uid u = getUidStatsLocked(uid);
+            final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
             u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
         });
 
@@ -12286,6 +12837,7 @@
     @VisibleForTesting
     public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
         final long startTimeMs = mClocks.uptimeMillis();
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
         mCpuUidClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
@@ -12298,7 +12850,7 @@
                 mCpuUidClusterTimeReader.removeUid(uid);
                 return;
             }
-            final Uid u = getUidStatsLocked(uid);
+            final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
             u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
         });
 
@@ -12338,8 +12890,8 @@
         m.arg1 = onBattery ? 1 : 0;
         mHandler.sendMessage(m);
 
-        final long uptime = mSecUptime * 1000;
-        final long realtime = mSecRealtime * 1000;
+        final long uptimeUs = mSecUptime * 1000;
+        final long realtimeUs = mSecRealtime * 1000;
         final int screenState = mScreenState;
         if (onBattery) {
             // We will reset our status if we are unplugging after the
@@ -12358,14 +12910,14 @@
                 // stats to be reported in the next checkin.  Only do this if we have
                 // a sufficient amount of data to make it interesting.
                 if (getLowDischargeAmountSinceCharge() >= 20) {
-                    final long startTime = SystemClock.uptimeMillis();
+                    final long startTimeMs = SystemClock.uptimeMillis();
                     final Parcel parcel = Parcel.obtain();
                     writeSummaryToParcel(parcel, true);
-                    final long initialTime = SystemClock.uptimeMillis() - startTime;
+                    final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs;
                     BackgroundThread.getHandler().post(new Runnable() {
                         @Override public void run() {
                             synchronized (mCheckinFile) {
-                                final long startTime2 = SystemClock.uptimeMillis();
+                                final long startTimeMs2 = SystemClock.uptimeMillis();
                                 FileOutputStream stream = null;
                                 try {
                                     stream = mCheckinFile.startWrite();
@@ -12373,8 +12925,8 @@
                                     stream.flush();
                                     mCheckinFile.finishWrite(stream);
                                     com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
-                                            "batterystats-checkin",
-                                            initialTime + SystemClock.uptimeMillis() - startTime2);
+                                            "batterystats-checkin", initialTimeMs
+                                            + SystemClock.uptimeMillis() - startTimeMs2);
                                 } catch (IOException e) {
                                     Slog.w("BatteryStats",
                                             "Error writing checkin battery statistics", e);
@@ -12387,7 +12939,7 @@
                     });
                 }
                 doWrite = true;
-                resetAllStatsLocked();
+                resetAllStatsLocked(mSecUptime, mSecRealtime);
                 if (chargeUAh > 0 && level > 0) {
                     // Only use the reported coulomb charge value if it is supported and reported.
                     mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0));
@@ -12434,7 +12986,7 @@
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenDoze = 0;
             mDischargeAmountScreenOff = 0;
-            updateTimeBasesLocked(true, screenState, uptime, realtime);
+            updateTimeBasesLocked(true, screenState, uptimeUs, realtimeUs);
         } else {
             mLastChargingStateLevel = level;
             mOnBattery = mOnBatteryInternal = false;
@@ -12450,14 +13002,14 @@
                 mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
             }
             updateDischargeScreenLevelsLocked(screenState, screenState);
-            updateTimeBasesLocked(false, screenState, uptime, realtime);
+            updateTimeBasesLocked(false, screenState, uptimeUs, realtimeUs);
             mChargeStepTracker.init();
             mLastChargeStepLevel = level;
             mMaxChargeStepLevel = level;
             mInitStepMode = mCurStepMode;
             mModStepMode = 0;
         }
-        if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
+        if (doWrite || (mLastWriteTimeMs + (60 * 1000)) < mSecRealtime) {
             if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
                 writeAsyncLocked();
             }
@@ -12477,18 +13029,18 @@
         }
     }
 
-    private void recordCurrentTimeChangeLocked(final long currentTime, final long elapsedRealtimeMs,
-            final long uptimeMs) {
+    private void recordCurrentTimeChangeLocked(final long currentTimeMs,
+            final long elapsedRealtimeMs, final long uptimeMs) {
         if (mRecordingHistory) {
-            mHistoryCur.currentTime = currentTime;
+            mHistoryCur.currentTime = currentTimeMs;
             addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
             mHistoryCur.currentTime = 0;
         }
     }
 
-    private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) {
+    private void recordShutdownLocked(final long currentTimeMs, final long elapsedRealtimeMs) {
         if (mRecordingHistory) {
-            mHistoryCur.currentTime = System.currentTimeMillis();
+            mHistoryCur.currentTime = currentTimeMs;
             addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
             mHistoryCur.currentTime = 0;
         }
@@ -12507,6 +13059,15 @@
     public void setBatteryStateLocked(final int status, final int health, final int plugType,
             final int level, /* not final */ int temp, final int volt, final int chargeUAh,
             final int chargeFullUAh, final long chargeTimeToFullSeconds) {
+        setBatteryStateLocked(status, health, plugType, level, temp, volt, chargeUAh,
+                chargeFullUAh, chargeTimeToFullSeconds,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis());
+    }
+
+    public void setBatteryStateLocked(final int status, final int health, final int plugType,
+            final int level, /* not final */ int temp, final int volt, final int chargeUAh,
+            final int chargeFullUAh, final long chargeTimeToFullSeconds,
+            final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) {
         // Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
         temp = Math.max(0, temp);
 
@@ -12514,8 +13075,6 @@
                 status, plugType, level);
 
         final boolean onBattery = isOnBattery(plugType, status);
-        final long uptime = mClocks.uptimeMillis();
-        final long elapsedRealtime = mClocks.elapsedRealtime();
         if (!mHaveBatteryLevel) {
             mHaveBatteryLevel = true;
             // We start out assuming that the device is plugged in (not
@@ -12538,20 +13097,20 @@
                     mLastChargeStepLevel = mLastDischargeStepLevel = level;
             mLastChargingStateLevel = level;
         } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
-            recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
+            recordDailyStatsIfNeededLocked(level >= 100 && onBattery, currentTimeMs);
         }
         int oldStatus = mHistoryCur.batteryStatus;
         if (onBattery) {
             mDischargeCurrentLevel = level;
             if (!mRecordingHistory) {
                 mRecordingHistory = true;
-                startRecordingHistory(elapsedRealtime, uptime, true);
+                startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
             }
         } else if (level < 96 &&
                 status != BatteryManager.BATTERY_STATUS_UNKNOWN) {
             if (!mRecordingHistory) {
                 mRecordingHistory = true;
-                startRecordingHistory(elapsedRealtime, uptime, true);
+                startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
             }
         }
         mCurrentBatteryLevel = level;
@@ -12581,7 +13140,7 @@
                 }
             }
             mHistoryCur.batteryChargeUAh = chargeUAh;
-            setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
+            setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUAh);
         } else {
             boolean changed = false;
             if (mHistoryCur.batteryLevel != level) {
@@ -12641,9 +13200,9 @@
                 changed |= setChargingLocked(false);
                 if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
                     mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
-                            modeBits, elapsedRealtime);
+                            modeBits, elapsedRealtimeMs);
                     mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
-                            modeBits, elapsedRealtime);
+                            modeBits, elapsedRealtimeMs);
                     mLastDischargeStepLevel = level;
                     mMinDischargeStepLevel = level;
                     mInitStepMode = mCurStepMode;
@@ -12681,9 +13240,9 @@
                 }
                 if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                     mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
-                            modeBits, elapsedRealtime);
+                            modeBits, elapsedRealtimeMs);
                     mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
-                            modeBits, elapsedRealtime);
+                            modeBits, elapsedRealtimeMs);
                     mMaxChargeStepLevel = level;
                     mInitStepMode = mCurStepMode;
                     mModStepMode = 0;
@@ -12691,7 +13250,7 @@
                 mLastChargeStepLevel = level;
             }
             if (changed) {
-                addHistoryRecordLocked(elapsedRealtime, uptime);
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
             }
         }
         if (!onBattery &&
@@ -12737,7 +13296,7 @@
     public long getAwakeTimeBattery() {
         // This previously evaluated to mOnBatteryTimeBase.getUptime(getBatteryUptimeLocked());
         // for over a decade, but surely that was a mistake.
-        return getBatteryUptimeLocked();
+        return getBatteryUptimeLocked(mClocks.uptimeMillis());
     }
 
     @UnsupportedAppUsage
@@ -12746,35 +13305,35 @@
     }
 
     @Override
-    public long computeUptime(long curTime, int which) {
-        return mUptime + (curTime - mUptimeStart);
+    public long computeUptime(long curTimeUs, int which) {
+        return mUptimeUs + (curTimeUs - mUptimeStartUs);
     }
 
     @Override
-    public long computeRealtime(long curTime, int which) {
-        return mRealtime + (curTime - mRealtimeStart);
+    public long computeRealtime(long curTimeUs, int which) {
+        return mRealtimeUs + (curTimeUs - mRealtimeStartUs);
     }
 
     @Override
     @UnsupportedAppUsage
-    public long computeBatteryUptime(long curTime, int which) {
-        return mOnBatteryTimeBase.computeUptime(curTime, which);
+    public long computeBatteryUptime(long curTimeUs, int which) {
+        return mOnBatteryTimeBase.computeUptime(curTimeUs, which);
     }
 
     @Override
     @UnsupportedAppUsage
-    public long computeBatteryRealtime(long curTime, int which) {
-        return mOnBatteryTimeBase.computeRealtime(curTime, which);
+    public long computeBatteryRealtime(long curTimeUs, int which) {
+        return mOnBatteryTimeBase.computeRealtime(curTimeUs, which);
     }
 
     @Override
-    public long computeBatteryScreenOffUptime(long curTime, int which) {
-        return mOnBatteryScreenOffTimeBase.computeUptime(curTime, which);
+    public long computeBatteryScreenOffUptime(long curTimeUs, int which) {
+        return mOnBatteryScreenOffTimeBase.computeUptime(curTimeUs, which);
     }
 
     @Override
-    public long computeBatteryScreenOffRealtime(long curTime, int which) {
-        return mOnBatteryScreenOffTimeBase.computeRealtime(curTime, which);
+    public long computeBatteryScreenOffRealtime(long curTimeUs, int which) {
+        return mOnBatteryScreenOffTimeBase.computeRealtime(curTimeUs, which);
     }
 
     private long computeTimePerLevel(long[] steps, int numSteps) {
@@ -12877,7 +13436,7 @@
     /*@hide */
     public CellularBatteryStats getCellularBatteryStats() {
         final int which = STATS_SINCE_CHARGED;
-        final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+        final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
         final ControllerActivityCounter counter = getModemControllerActivity();
         final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
@@ -12887,13 +13446,13 @@
                 counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
         long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES];
         for (int i = 0; i < timeInRatMs.length; i++) {
-           timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000;
+            timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTimeUs, which) / 1000;
         }
         long[] timeInRxSignalStrengthLevelMs =
                 new long[CellSignalStrength.getNumSignalStrengthLevels()];
         for (int i = 0; i < timeInRxSignalStrengthLevelMs.length; i++) {
-           timeInRxSignalStrengthLevelMs[i]
-               = getPhoneSignalStrengthTime(i, rawRealTime, which) / 1000;
+            timeInRxSignalStrengthLevelMs[i] =
+                getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
         }
         long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS,
             counter.getTxTimeCounters().length)];
@@ -12903,8 +13462,8 @@
             totalTxTimeMs += txTimeMs[i];
         }
 
-        return new CellularBatteryStats(computeBatteryRealtime(rawRealTime, which) / 1000,
-                getMobileRadioActiveTime(rawRealTime, which) / 1000,
+        return new CellularBatteryStats(computeBatteryRealtime(rawRealTimeUs, which) / 1000,
+                getMobileRadioActiveTime(rawRealTimeUs, which) / 1000,
                 getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which),
                 getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which),
                 getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which),
@@ -12917,7 +13476,7 @@
     /*@hide */
     public WifiBatteryStats getWifiBatteryStats() {
         final int which = STATS_SINCE_CHARGED;
-        final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+        final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
         final ControllerActivityCounter counter = getWifiControllerActivity();
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
         final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
@@ -12936,19 +13495,19 @@
         }
         long[] timeInStateMs = new long[NUM_WIFI_STATES];
         for (int i=0; i<NUM_WIFI_STATES; i++) {
-            timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
+            timeInStateMs[i] = getWifiStateTime(i, rawRealTimeUs, which) / 1000;
         }
         long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
         for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
-            timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+            timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTimeUs, which) / 1000;
         }
         long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
-            timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+            timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
         }
         return new WifiBatteryStats(
-                computeBatteryRealtime(rawRealTime, which) / 1000,
-                getWifiActiveTime(rawRealTime, which) / 1000,
+                computeBatteryRealtime(rawRealTimeUs, which) / 1000,
+                getWifiActiveTime(rawRealTimeUs, which) / 1000,
                 getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which),
                 getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which),
                 getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which),
@@ -12962,12 +13521,12 @@
     public GpsBatteryStats getGpsBatteryStats() {
         GpsBatteryStats s = new GpsBatteryStats();
         final int which = STATS_SINCE_CHARGED;
-        final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
-        s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+        final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
+        s.setLoggingDurationMs(computeBatteryRealtime(rawRealTimeUs, which) / 1000);
         s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs());
         long[] time = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
         for (int i=0; i<time.length; i++) {
-            time[i] = getGpsSignalQualityTime(i, rawRealTime, which) / 1000;
+            time[i] = getGpsSignalQualityTime(i, rawRealTimeUs, which) / 1000;
         }
         s.setTimeInGpsSignalQualityLevel(time);
         return s;
@@ -12988,19 +13547,29 @@
         return mDailyPackageChanges;
     }
 
+    /**
+     * @return battery uptime in microseconds
+     */
     protected long getBatteryUptimeLocked() {
-        return mOnBatteryTimeBase.getUptime(mClocks.uptimeMillis() * 1000);
+        return getBatteryUptimeLocked(mClocks.uptimeMillis());
+    }
+
+    /**
+     * @return battery uptime in microseconds
+     */
+    protected long getBatteryUptimeLocked(long uptimeMs) {
+        return mOnBatteryTimeBase.getUptime(uptimeMs * 1000);
     }
 
     @Override
-    public long getBatteryUptime(long curTime) {
-        return mOnBatteryTimeBase.getUptime(curTime);
+    public long getBatteryUptime(long curTimeUs) {
+        return mOnBatteryTimeBase.getUptime(curTimeUs);
     }
 
     @Override
     @UnsupportedAppUsage
-    public long getBatteryRealtime(long curTime) {
-        return mOnBatteryTimeBase.getRealtime(curTime);
+    public long getBatteryRealtime(long curTimeUs) {
+        return mOnBatteryTimeBase.getRealtime(curTimeUs);
     }
 
     @Override
@@ -13188,22 +13757,22 @@
             return 0;
         }
 
-        final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step,
+        final long uidTimeAtCpuSpeedUs = systemUid.getTimeAtCpuSpeed(cluster, step,
                 BatteryStats.STATS_SINCE_CHARGED);
-        if (uidTimeAtCpuSpeed == 0) {
+        if (uidTimeAtCpuSpeedUs == 0) {
             return 0;
         }
 
-        final long uidThreadTime =
+        final long uidThreadTimeUs =
                 threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
 
-        if (uidThreadTime == 0) {
+        if (uidThreadTimeUs == 0) {
             return 0;
         }
 
-        final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
+        final long binderThreadTimeUs = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
                 BatteryStats.STATS_SINCE_CHARGED);
-        return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime;
+        return uidTimeAtCpuSpeedUs * binderThreadTimeUs / uidThreadTimeUs;
     }
 
     /**
@@ -13211,9 +13780,13 @@
      */
     @UnsupportedAppUsage
     public Uid getUidStatsLocked(int uid) {
+        return getUidStatsLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public Uid getUidStatsLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
         Uid u = mUidStats.get(uid);
         if (u == null) {
-            u = new Uid(this, uid);
+            u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
             mUidStats.put(uid, u);
         }
         return u;
@@ -13229,10 +13802,14 @@
     }
 
     public void onCleanupUserLocked(int userId) {
+        onCleanupUserLocked(userId, mClocks.elapsedRealtime());
+    }
+
+    public void onCleanupUserLocked(int userId, long elapsedRealtimeMs) {
         final int firstUidForUser = UserHandle.getUid(userId, 0);
         final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
         mPendingRemovedUids.add(
-                new UidToRemove(firstUidForUser, lastUidForUser, mClocks.elapsedRealtime()));
+                new UidToRemove(firstUidForUser, lastUidForUser, elapsedRealtimeMs));
     }
 
     public void onUserRemovedLocked(int userId) {
@@ -13256,12 +13833,19 @@
      */
     @UnsupportedAppUsage
     public void removeUidStatsLocked(int uid) {
+        removeUidStatsLocked(uid, mClocks.elapsedRealtime());
+    }
+
+    /**
+     * @see #removeUidStatsLocked(int)
+     */
+    public void removeUidStatsLocked(int uid, long elapsedRealtimeMs) {
         final Uid u = mUidStats.get(uid);
         if (u != null) {
             u.detachFromTimeBase();
         }
         mUidStats.remove(uid);
-        mPendingRemovedUids.add(new UidToRemove(uid, mClocks.elapsedRealtime()));
+        mPendingRemovedUids.add(new UidToRemove(uid, elapsedRealtimeMs));
     }
 
     /**
@@ -13270,8 +13854,16 @@
      */
     @UnsupportedAppUsage
     public Uid.Proc getProcessStatsLocked(int uid, String name) {
+        return getProcessStatsLocked(uid, name, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    /**
+     * @see #getProcessStatsLocked(int, String)
+     */
+    public Uid.Proc getProcessStatsLocked(int uid, String name,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        Uid u = getUidStatsLocked(uid);
+        Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
         return u.getProcessStatsLocked(name);
     }
 
@@ -13281,8 +13873,16 @@
      */
     @UnsupportedAppUsage
     public Uid.Pkg getPackageStatsLocked(int uid, String pkg) {
+        return getPackageStatsLocked(uid, pkg, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    /**
+     * @see getPackageStatsLocked(int, String)
+     */
+    public Uid.Pkg getPackageStatsLocked(int uid, String pkg,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        Uid u = getUidStatsLocked(uid);
+        Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
         return u.getPackageStatsLocked(pkg);
     }
 
@@ -13292,13 +13892,19 @@
      */
     @UnsupportedAppUsage
     public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) {
+        return getServiceStatsLocked(uid, pkg, name,
+                mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+    }
+
+    public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name,
+            long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        Uid u = getUidStatsLocked(uid);
+        Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
         return u.getServiceStatsLocked(pkg, name);
     }
 
     public void shutdownLocked() {
-        recordShutdownLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+        recordShutdownLocked(System.currentTimeMillis(), mClocks.elapsedRealtime());
         writeSyncLocked();
         mShuttingDown = true;
     }
@@ -13462,7 +14068,7 @@
 
                 mNumSingleUidCpuTimeReads = 0;
                 mNumBatchedSingleUidCpuTimeReads = 0;
-                mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
+                mCpuTimeReadsTrackingStartTimeMs = mClocks.uptimeMillis();
             }
         }
 
@@ -13471,7 +14077,7 @@
             if (oldDelayMillis != newDelayMillis) {
                 mNumSingleUidCpuTimeReads = 0;
                 mNumBatchedSingleUidCpuTimeReads = 0;
-                mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
+                mCpuTimeReadsTrackingStartTimeMs = mClocks.uptimeMillis();
             }
         }
 
@@ -13521,7 +14127,11 @@
 
     @GuardedBy("this")
     public void dumpConstantsLocked(PrintWriter pw) {
-        mConstants.dumpLocked(pw);
+        final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, "    ");
+        iPw.println("BatteryStats constants:");
+        iPw.increaseIndent();
+        mConstants.dumpLocked(iPw);
+        iPw.decreaseIndent();
     }
 
     @GuardedBy("this")
@@ -13569,11 +14179,11 @@
                 Uid uid = mUidStats.get(u);
                 double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
 
-                long time = 0;
+                long timeUs = 0;
                 for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) {
                     int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length;
                     for (int speed = 0; speed < numSpeeds; speed++) {
-                        time += getSystemServiceTimeAtCpuSpeed(cluster, speed)
+                        timeUs += getSystemServiceTimeAtCpuSpeed(cluster, speed)
                                 * proportionalSystemServiceUsage;
                     }
                 }
@@ -13581,7 +14191,7 @@
                 pw.print("  ");
                 pw.print(u);
                 pw.print(": ");
-                pw.println(time);
+                pw.println(timeUs / 1000);
             }
         }
     }
@@ -13616,7 +14226,7 @@
             Slog.d(TAG, "writeSummaryToParcel duration ms:"
                     + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
         }
-        mLastWriteTime = mClocks.elapsedRealtime();
+        mLastWriteTimeMs = mClocks.elapsedRealtime();
         writeParcelToFileLocked(p, mStatsFile, sync);
     }
 
@@ -13657,18 +14267,18 @@
         mWriteLock.lock();
         FileOutputStream fos = null;
         try {
-            final long startTime = SystemClock.uptimeMillis();
+            final long startTimeMs = SystemClock.uptimeMillis();
             fos = file.startWrite();
             fos.write(p.marshall());
             fos.flush();
             file.finishWrite(fos);
             if (DEBUG) {
                 Slog.d(TAG, "commitPendingDataToDisk file:" + file.getBaseFile().getPath()
-                        + " duration ms:" + (SystemClock.uptimeMillis() - startTime)
+                        + " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs)
                         + " bytes:" + p.dataSize());
             }
             com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
-                    "batterystats", SystemClock.uptimeMillis() - startTime);
+                    "batterystats", SystemClock.uptimeMillis() - startTimeMs);
         } catch (IOException e) {
             Slog.w(TAG, "Error writing battery statistics", e);
             file.failWrite(fos);
@@ -13714,7 +14324,7 @@
             }
         } catch (Exception e) {
             Slog.e(TAG, "Error reading battery statistics", e);
-            resetAllStatsLocked();
+            resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());
         } finally {
             stats.recycle();
         }
@@ -13749,16 +14359,17 @@
         if (mHistoryBuffer.dataPosition() > 0
                 || mBatteryStatsHistory.getFilesNumbers().size() > 1) {
             mRecordingHistory = true;
-            final long elapsedRealtime = mClocks.elapsedRealtime();
-            final long uptime = mClocks.uptimeMillis();
+            final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+            final long uptimeMs = mClocks.uptimeMillis();
             if (USE_OLD_HISTORY) {
-                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+                addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs,
+                        HistoryItem.CMD_START, mHistoryCur);
             }
-            addHistoryBufferLocked(elapsedRealtime, HistoryItem.CMD_START, mHistoryCur);
-            startRecordingHistory(elapsedRealtime, uptime, false);
+            addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_START, mHistoryCur);
+            startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
         }
 
-        recordDailyStatsIfNeededLocked(false);
+        recordDailyStatsIfNeededLocked(false, System.currentTimeMillis());
     }
 
     public int describeContents() {
@@ -13799,27 +14410,27 @@
 
         if (DEBUG_HISTORY) {
             StringBuilder sb = new StringBuilder(128);
-            sb.append("****************** OLD mHistoryBaseTime: ");
-            TimeUtils.formatDuration(mHistoryBaseTime, sb);
+            sb.append("****************** OLD mHistoryBaseTimeMs: ");
+            TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
             Slog.i(TAG, sb.toString());
         }
-        mHistoryBaseTime = historyBaseTime;
+        mHistoryBaseTimeMs = historyBaseTime;
         if (DEBUG_HISTORY) {
             StringBuilder sb = new StringBuilder(128);
-            sb.append("****************** NEW mHistoryBaseTime: ");
-            TimeUtils.formatDuration(mHistoryBaseTime, sb);
+            sb.append("****************** NEW mHistoryBaseTimeMs: ");
+            TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
             Slog.i(TAG, sb.toString());
         }
 
         // We are just arbitrarily going to insert 1 minute from the sample of
         // the last run until samples in this run.
-        if (mHistoryBaseTime > 0) {
+        if (mHistoryBaseTimeMs > 0) {
             long oldnow = mClocks.elapsedRealtime();
-            mHistoryBaseTime = mHistoryBaseTime - oldnow + 1;
+            mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
             if (DEBUG_HISTORY) {
                 StringBuilder sb = new StringBuilder(128);
-                sb.append("****************** ADJUSTED mHistoryBaseTime: ");
-                TimeUtils.formatDuration(mHistoryBaseTime, sb);
+                sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
+                TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
                 Slog.i(TAG, sb.toString());
             }
         }
@@ -13839,14 +14450,14 @@
     void writeHistoryBuffer(Parcel out, boolean inclData, boolean andOldHistory) {
         if (DEBUG_HISTORY) {
             StringBuilder sb = new StringBuilder(128);
-            sb.append("****************** WRITING mHistoryBaseTime: ");
-            TimeUtils.formatDuration(mHistoryBaseTime, sb);
-            sb.append(" mLastHistoryElapsedRealtime: ");
-            TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
+            sb.append("****************** WRITING mHistoryBaseTimeMs: ");
+            TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+            sb.append(" mLastHistoryElapsedRealtimeMs: ");
+            TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
             Slog.i(TAG, sb.toString());
         }
         out.writeInt(VERSION);
-        out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
+        out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
         if (!inclData) {
             out.writeInt(0);
             out.writeInt(0);
@@ -13913,9 +14524,9 @@
         }
 
         mStartCount = in.readInt();
-        mUptime = in.readLong();
-        mRealtime = in.readLong();
-        mStartClockTime = in.readLong();
+        mUptimeUs = in.readLong();
+        mRealtimeUs = in.readLong();
+        mStartClockTimeMs = in.readLong();
         mStartPlatformVersion = in.readString();
         mEndPlatformVersion = in.readString();
         mOnBatteryTimeBase.readSummaryFromParcel(in);
@@ -13955,9 +14566,9 @@
         } else {
             mDailyPackageChanges = null;
         }
-        mDailyStartTime = in.readLong();
-        mNextMinDailyDeadline = in.readLong();
-        mNextMaxDailyDeadline = in.readLong();
+        mDailyStartTimeMs = in.readLong();
+        mNextMinDailyDeadlineMs = in.readLong();
+        mNextMaxDailyDeadlineMs = in.readLong();
 
         mStartCount++;
 
@@ -13971,8 +14582,8 @@
         mInteractiveTimer.readSummaryFromParcelLocked(in);
         mPhoneOn = false;
         mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
-        mLongestLightIdleTime = in.readLong();
-        mLongestFullIdleTime = in.readLong();
+        mLongestLightIdleTimeMs = in.readLong();
+        mLongestFullIdleTimeMs = in.readLong();
         mDeviceIdleModeLightTimer.readSummaryFromParcelLocked(in);
         mDeviceIdleModeFullTimer.readSummaryFromParcelLocked(in);
         mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in);
@@ -14084,9 +14695,11 @@
         if (NU > 10000) {
             throw new ParcelFormatException("File corrupt: too many uids " + NU);
         }
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+        final long uptimeMs = mClocks.uptimeMillis();
         for (int iu = 0; iu < NU; iu++) {
             int uid = in.readInt();
-            Uid u = new Uid(this, uid);
+            Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
             mUidStats.put(uid, u);
 
             u.mOnBatteryBackgroundTimeBase.readSummaryFromParcel(in);
@@ -14329,9 +14942,9 @@
             for (int ip = 0; ip < NP; ip++) {
                 String procName = in.readString();
                 Uid.Proc p = u.getProcessStatsLocked(procName);
-                p.mUserTime = in.readLong();
-                p.mSystemTime = in.readLong();
-                p.mForegroundTime = in.readLong();
+                p.mUserTimeMs = in.readLong();
+                p.mSystemTimeMs = in.readLong();
+                p.mForegroundTimeMs = in.readLong();
                 p.mStarts = in.readInt();
                 p.mNumCrashes = in.readInt();
                 p.mNumAnrs = in.readInt();
@@ -14364,7 +14977,7 @@
                 for (int is = 0; is < NS; is++) {
                     String servName = in.readString();
                     Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName);
-                    s.mStartTime = in.readLong();
+                    s.mStartTimeMs = in.readLong();
                     s.mStarts = in.readInt();
                     s.mLaunches = in.readInt();
                 }
@@ -14407,7 +15020,7 @@
         out.writeInt(mStartCount);
         out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
-        out.writeLong(mStartClockTime);
+        out.writeLong(mStartClockTimeMs);
         out.writeString(mStartPlatformVersion);
         out.writeString(mEndPlatformVersion);
         mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
@@ -14445,9 +15058,9 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(mDailyStartTime);
-        out.writeLong(mNextMinDailyDeadline);
-        out.writeLong(mNextMaxDailyDeadline);
+        out.writeLong(mDailyStartTimeMs);
+        out.writeLong(mNextMinDailyDeadlineMs);
+        out.writeLong(mNextMaxDailyDeadlineMs);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -14456,8 +15069,8 @@
         }
         mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-        out.writeLong(mLongestLightIdleTime);
-        out.writeLong(mLongestFullIdleTime);
+        out.writeLong(mLongestLightIdleTimeMs);
+        out.writeLong(mLongestFullIdleTimeMs);
         mDeviceIdleModeLightTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mDeviceIdleModeFullTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -14852,9 +15465,9 @@
             for (int ip=0; ip<NP; ip++) {
                 out.writeString(u.mProcessStats.keyAt(ip));
                 Uid.Proc ps = u.mProcessStats.valueAt(ip);
-                out.writeLong(ps.mUserTime);
-                out.writeLong(ps.mSystemTime);
-                out.writeLong(ps.mForegroundTime);
+                out.writeLong(ps.mUserTimeMs);
+                out.writeLong(ps.mSystemTimeMs);
+                out.writeLong(ps.mForegroundTimeMs);
                 out.writeInt(ps.mStarts);
                 out.writeInt(ps.mNumCrashes);
                 out.writeInt(ps.mNumAnrs);
@@ -14880,7 +15493,7 @@
                         out.writeString(ps.mServiceStats.keyAt(is));
                         BatteryStatsImpl.Uid.Pkg.Serv ss = ps.mServiceStats.valueAt(is);
                         long time = ss.getStartTimeToNowLocked(
-                                mOnBatteryTimeBase.getUptime(NOW_SYS));
+                                mOnBatteryTimeBase.getUptime(NOW_SYS) / 1000);
                         out.writeLong(time);
                         out.writeInt(ss.mStarts);
                         out.writeInt(ss.mLaunches);
@@ -14904,13 +15517,13 @@
         mBatteryStatsHistory.readFromParcel(in);
 
         mStartCount = in.readInt();
-        mStartClockTime = in.readLong();
+        mStartClockTimeMs = in.readLong();
         mStartPlatformVersion = in.readString();
         mEndPlatformVersion = in.readString();
-        mUptime = in.readLong();
-        mUptimeStart = in.readLong();
-        mRealtime = in.readLong();
-        mRealtimeStart = in.readLong();
+        mUptimeUs = in.readLong();
+        mUptimeStartUs = in.readLong();
+        mRealtimeUs = in.readLong();
+        mRealtimeStartUs = in.readLong();
         mOnBattery = in.readInt() != 0;
         mEstimatedBatteryCapacity = in.readInt();
         mMinLearnedBatteryCapacity = in.readInt();
@@ -14931,8 +15544,8 @@
         mPhoneOn = false;
         mPowerSaveModeEnabledTimer = new StopwatchTimer(mClocks, null, -2, null,
                 mOnBatteryTimeBase, in);
-        mLongestLightIdleTime = in.readLong();
-        mLongestFullIdleTime = in.readLong();
+        mLongestLightIdleTimeMs = in.readLong();
+        mLongestFullIdleTimeMs = in.readLong();
         mDeviceIdleModeLightTimer = new StopwatchTimer(mClocks, null, -14, null,
                 mOnBatteryTimeBase, in);
         mDeviceIdleModeFullTimer = new StopwatchTimer(mClocks, null, -11, null,
@@ -15030,7 +15643,7 @@
         mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
-        mLastWriteTime = in.readLong();
+        mLastWriteTimeMs = in.readLong();
 
         mRpmStats.clear();
         int NRPMS = in.readInt();
@@ -15096,9 +15709,11 @@
 
         int numUids = in.readInt();
         mUidStats.clear();
+        final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+        final long uptimeMs = mClocks.uptimeMillis();
         for (int i = 0; i < numUids; i++) {
             int uid = in.readInt();
-            Uid u = new Uid(this, uid);
+            Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
             u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in);
             mUidStats.append(uid, u);
         }
@@ -15137,13 +15752,13 @@
         mBatteryStatsHistory.writeToParcel(out);
 
         out.writeInt(mStartCount);
-        out.writeLong(mStartClockTime);
+        out.writeLong(mStartClockTimeMs);
         out.writeString(mStartPlatformVersion);
         out.writeString(mEndPlatformVersion);
-        out.writeLong(mUptime);
-        out.writeLong(mUptimeStart);
-        out.writeLong(mRealtime);
-        out.writeLong(mRealtimeStart);
+        out.writeLong(mUptimeUs);
+        out.writeLong(mUptimeStartUs);
+        out.writeLong(mRealtimeUs);
+        out.writeLong(mRealtimeStartUs);
         out.writeInt(mOnBattery ? 1 : 0);
         out.writeInt(mEstimatedBatteryCapacity);
         out.writeInt(mMinLearnedBatteryCapacity);
@@ -15158,8 +15773,8 @@
         }
         mInteractiveTimer.writeToParcel(out, uSecRealtime);
         mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
-        out.writeLong(mLongestLightIdleTime);
-        out.writeLong(mLongestFullIdleTime);
+        out.writeLong(mLongestLightIdleTimeMs);
+        out.writeLong(mLongestFullIdleTimeMs);
         mDeviceIdleModeLightTimer.writeToParcel(out, uSecRealtime);
         mDeviceIdleModeFullTimer.writeToParcel(out, uSecRealtime);
         mDeviceLightIdlingTimer.writeToParcel(out, uSecRealtime);
@@ -15227,7 +15842,7 @@
         mDischargeScreenDozeCounter.writeToParcel(out);
         mDischargeLightDozeCounter.writeToParcel(out);
         mDischargeDeepDozeCounter.writeToParcel(out);
-        out.writeLong(mLastWriteTime);
+        out.writeLong(mLastWriteTimeMs);
 
         out.writeInt(mRpmStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
@@ -15467,15 +16082,19 @@
             mCameraOnTimer.logState(pr, "  ");
         }
         super.dumpLocked(context, pw, flags, reqUid, histStart);
+
         pw.print("Total cpu time reads: ");
         pw.println(mNumSingleUidCpuTimeReads);
         pw.print("Batched cpu time reads: ");
         pw.println(mNumBatchedSingleUidCpuTimeReads);
         pw.print("Batching Duration (min): ");
-        pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTime) / (60 * 1000));
+        pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
         pw.print("All UID cpu time reads since the later of device start or stats reset: ");
         pw.println(mNumAllUidCpuTimeReads);
         pw.print("UIDs removed since the later of device start or stats reset: ");
         pw.println(mNumUidsRemoved);
+
+        pw.println();
+        dumpConstantsLocked(pw);
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 32e7fdc..5948e7e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -86,9 +86,10 @@
  */
 public class ZygoteInit {
 
-    // TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
     private static final String TAG = "Zygote";
 
+    private static final boolean LOGGING_DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
     private static final String PROPERTY_DISABLE_GRAPHICS_DRIVER_PRELOADING =
             "ro.zygote.disable_gl_preload";
 
@@ -284,6 +285,7 @@
                     new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
 
             int count = 0;
+            int missingLambdaCount = 0;
             String line;
             while ((line = br.readLine()) != null) {
                 // Skip comments and blank lines.
@@ -302,24 +304,33 @@
                     Class.forName(line, true, null);
                     count++;
                 } catch (ClassNotFoundException e) {
-                    Log.w(TAG, "Class not found for preloading: " + line);
+                    if (line.contains("$$Lambda$")) {
+                        if (LOGGING_DEBUG) {
+                            missingLambdaCount++;
+                        }
+                    } else {
+                        Log.w(TAG, "Class not found for preloading: " + line);
+                    }
                 } catch (UnsatisfiedLinkError e) {
                     Log.w(TAG, "Problem preloading " + line + ": " + e);
                 } catch (Throwable t) {
                     Log.e(TAG, "Error preloading " + line + ".", t);
                     if (t instanceof Error) {
                         throw (Error) t;
-                    }
-                    if (t instanceof RuntimeException) {
+                    } else if (t instanceof RuntimeException) {
                         throw (RuntimeException) t;
+                    } else {
+                        throw new RuntimeException(t);
                     }
-                    throw new RuntimeException(t);
                 }
                 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
             }
 
             Log.i(TAG, "...preloaded " + count + " classes in "
                     + (SystemClock.uptimeMillis() - startTime) + "ms.");
+            if (LOGGING_DEBUG && missingLambdaCount != 0) {
+                Log.i(TAG, "Unresolved lambda preloads: " + missingLambdaCount);
+            }
         } catch (IOException e) {
             Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
         } finally {
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 7f3eb45..d5f54a1 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -18,13 +18,11 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
-import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IScrollCaptureController;
 import android.view.IWindow;
@@ -33,6 +31,7 @@
 import android.view.InsetsState;
 import android.view.PointerIcon;
 import android.view.WindowInsets.Type.InsetsType;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -51,11 +50,9 @@
     }
 
     @Override
-    public void resized(Rect frame, Rect contentInsets, Rect visibleInsets,
-            Rect stableInsets, boolean reportDraw,
-            MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeSystemBars, int displayId,
-            DisplayCutout.ParcelableWrapper displayCutout) {
+    public void resized(ClientWindowFrames frames, boolean reportDraw,
+            MergedConfiguration mergedConfiguration, boolean forceLayout,
+            boolean alwaysConsumeSystemBars, int displayId) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this, null /* postDrawTransaction */);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index c51f334..c08363b 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -19,18 +19,16 @@
 
 // To make sure cpu_set_t is included from sched.h
 #define _GNU_SOURCE 1
-#include <android-base/properties.h>
-#include <android-base/unique_fd.h>
-#include <binder/ActivityManager.h>
+#include <utils/Log.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
 #include <processgroup/sched_policy.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
+#include <android-base/unique_fd.h>
 
 #include <algorithm>
 #include <array>
@@ -86,8 +84,6 @@
 static pthread_key_t gBgKey = -1;
 #endif
 
-static bool boot_completed = false;
-
 // For both of these, err should be in the errno range (positive), not a status_t (negative)
 static void signalExceptionForError(JNIEnv* env, int err, int tid) {
     switch (err) {
@@ -584,20 +580,7 @@
     }
 #endif
 
-    SchedPolicy policy;
-    bool policy_changed = false;
-    int rc = 0, curr_pri = getpriority(PRIO_PROCESS, pid);
-
-    if (pri == curr_pri) {
-        return;
-    }
-
-    if (!boot_completed) {
-        boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
-    }
-
-    // Do not change sched policy cgroup after boot complete.
-    rc = androidSetThreadPriorityAndPolicy(pid, pri, !boot_completed);
+    int rc = androidSetThreadPriority(pid, pri);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
             signalExceptionForPriorityError(env, errno, pid);
@@ -606,31 +589,6 @@
         }
     }
 
-    // Only use async approach after boot complete.
-    if (!boot_completed) {
-        return;
-    }
-
-    // Change to background sched policy for the thread if setting to low priority.
-    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
-        policy = SP_BACKGROUND;
-        policy_changed = true;
-        // Change to sched policy of the process if thread priority is raising from low priority.
-    } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
-        // If we cannot get sched policy of the process, use SP_FOREGROUND as default.
-        policy = SP_FOREGROUND;
-        get_sched_policy(getpid(), &policy);
-        policy_changed = true;
-    }
-
-    // Sched policy will only change in above 2 cases.
-    if (policy_changed) {
-        ActivityManager am;
-        if (!am.setSchedPolicyCgroup(pid, policy)) {
-            ALOGE("am.setThreadPriority failed: tid=%d priority=%d policy=%d", pid, pri, policy);
-        }
-    }
-
     //ALOGI("Setting priority of %" PRId32 ": %" PRId32 ", getpriority returns %d\n",
     //     pid, pri, getpriority(PRIO_PROCESS, pid));
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 416f837..4ef15d7 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -30,7 +30,6 @@
 #include <android_runtime/android_hardware_HardwareBuffer.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_view_SurfaceSession.h>
-#include <gui/IScreenCaptureListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -189,11 +188,6 @@
 
 static struct {
     jclass clazz;
-    jmethodID onScreenCaptureComplete;
-} gScreenCaptureListenerClassInfo;
-
-static struct {
-    jclass clazz;
     jmethodID ctor;
     jfieldID defaultConfig;
     jfieldID primaryRefreshRateMin;
@@ -232,54 +226,6 @@
     }
 }
 
-class ScreenCaptureListenerWrapper : public BnScreenCaptureListener {
-public:
-    explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
-        env->GetJavaVM(&mVm);
-        screenCaptureListenerObject = env->NewGlobalRef(jobject);
-        LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref");
-    }
-
-    ~ScreenCaptureListenerWrapper() {
-        if (screenCaptureListenerObject) {
-            getenv()->DeleteGlobalRef(screenCaptureListenerObject);
-            screenCaptureListenerObject = nullptr;
-        }
-    }
-
-    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) {
-        JNIEnv* env = getenv();
-        if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) {
-            env->CallVoidMethod(screenCaptureListenerObject,
-                                gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
-            return NO_ERROR;
-        }
-        jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
-                env, captureResults.buffer->toAHardwareBuffer());
-        const jint namedColorSpace =
-                fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
-        jobject screenshotHardwareBuffer =
-                env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
-                                            gScreenshotHardwareBufferClassInfo.builder,
-                                            jhardwareBuffer, namedColorSpace,
-                                            captureResults.capturedSecureLayers);
-        env->CallVoidMethod(screenCaptureListenerObject,
-                            gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
-                            screenshotHardwareBuffer);
-        return NO_ERROR;
-    }
-
-private:
-    jobject screenCaptureListenerObject;
-    JavaVM* mVm;
-
-    JNIEnv* getenv() {
-        JNIEnv* env;
-        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
-        return env;
-    }
-};
-
 // ----------------------------------------------------------------------------
 
 static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -381,28 +327,36 @@
     return captureArgs;
 }
 
-static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
-                                 jobject screenCaptureListenerObject) {
+static jobject nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject) {
     const DisplayCaptureArgs captureArgs =
             displayCaptureArgsFromObject(env, displayCaptureArgsObject);
 
     if (captureArgs.displayToken == NULL) {
-        return BAD_VALUE;
+        return NULL;
     }
 
-    sp<IScreenCaptureListener> captureListener =
-            new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
-    return ScreenshotClient::captureDisplay(captureArgs, captureListener);
+    ScreenCaptureResults captureResults;
+    status_t res = ScreenshotClient::captureDisplay(captureArgs, captureResults);
+    if (res != NO_ERROR) {
+        return NULL;
+    }
+
+    jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+            env, captureResults.buffer->toAHardwareBuffer());
+    const jint namedColorSpace =
+            fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
+    return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+                                       gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
+                                       namedColorSpace, captureResults.capturedSecureLayers);
 }
 
-static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
-                                jobject screenCaptureListenerObject) {
+static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject) {
     LayerCaptureArgs captureArgs;
     getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
     SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
             env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
     if (layer == nullptr) {
-        return BAD_VALUE;
+        return nullptr;
     }
 
     captureArgs.layerHandle = layer->getHandle();
@@ -426,9 +380,19 @@
         env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT);
     }
 
-    sp<IScreenCaptureListener> captureListener =
-            new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
-    return ScreenshotClient::captureLayers(captureArgs, captureListener);
+    ScreenCaptureResults captureResults;
+    status_t res = ScreenshotClient::captureLayers(captureArgs, captureResults);
+    if (res != NO_ERROR) {
+        return NULL;
+    }
+
+    jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+            env, captureResults.buffer->toAHardwareBuffer());
+    const jint namedColorSpace =
+            fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
+    return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+                                       gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
+                                       namedColorSpace, captureResults.capturedSecureLayers);
 }
 
 static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
@@ -1541,10 +1505,24 @@
     return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
 }
 
+static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                   jobject toTokenObj, jobject focusedTokenObj, jint displayId) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    if (toTokenObj == NULL) return;
+
+    sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
+    sp<IBinder> focusedToken;
+    if (focusedTokenObj != NULL) {
+        focusedToken = ibinderForJavaObject(env, focusedTokenObj);
+    }
+    transaction->setFocusedWindow(toToken, focusedToken, systemTime(SYSTEM_TIME_MONOTONIC),
+                                  displayId);
+}
+
 // ----------------------------------------------------------------------------
 
-// clang-format off
 static const JNINativeMethod sSurfaceControlMethods[] = {
+        // clang-format off
     {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
             (void*)nativeCreate },
     {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
@@ -1686,10 +1664,12 @@
     {"nativeSetOverrideScalingMode", "(JJI)V",
             (void*)nativeSetOverrideScalingMode },
     {"nativeCaptureDisplay",
-            "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
+            "(Landroid/view/SurfaceControl$DisplayCaptureArgs;)"
+            "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
             (void*)nativeCaptureDisplay },
     {"nativeCaptureLayers",
-            "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
+            "(Landroid/view/SurfaceControl$LayerCaptureArgs;)"
+            "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
             (void*)nativeCaptureLayers },
     {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
             (void*)nativeSetInputWindowInfo },
@@ -1721,9 +1701,12 @@
             (void*)nativeSetGlobalShadowSettings },
     {"nativeGetHandle", "(J)J",
             (void*)nativeGetHandle },
-    {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint},
+    {"nativeSetFixedTransformHint", "(JJI)V",
+            (void*)nativeSetFixedTransformHint},
+    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Landroid/os/IBinder;I)V",
+            (void*)nativeSetFocusedWindow},
+        // clang-format on
 };
-// clang-format on
 
 int register_android_view_SurfaceControl(JNIEnv* env)
 {
@@ -1892,12 +1875,6 @@
     gLayerCaptureArgsClassInfo.childrenOnly =
             GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
 
-    jclass screenCaptureListenerClazz =
-            FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener");
-    gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
-    gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
-            GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
-                             "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
     return err;
 }
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 3d12d07..7f743ef 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -219,6 +219,8 @@
 
     optional SettingProto enhanced_voice_privacy_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    optional SettingProto force_bold_text = 85 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message Gesture {
         optional SettingProto aware_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
@@ -615,5 +617,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 85;
+    // Next tag = 86;
 }
diff --git a/core/proto/android/wifi/enums.proto b/core/proto/android/wifi/enums.proto
index 315c579..e676fef 100644
--- a/core/proto/android/wifi/enums.proto
+++ b/core/proto/android/wifi/enums.proto
@@ -48,3 +48,65 @@
      */
     WIFI_MODE_FULL_LOW_LATENCY = 4;
 }
+
+/**
+ * Wifi authentication type.
+ */
+enum WifiAuthType {
+    AUTH_TYPE_NONE = 0;
+
+    // WPA pre-shared key.
+    AUTH_TYPE_WPA_PSK = 1;
+    // WPA using EAP authentication. Generally used with an external authentication server.
+    AUTH_TYPE_WPA_EAP = 2;
+    // IEEE 802.1X using EAP authentication and (optionally) dynamically generated WEP keys.
+    AUTH_TYPE_IEEE8021X = 3;
+    // WPA2 pre-shared key for use with soft access point.
+    AUTH_TYPE_WPA2_PSK = 4;
+    // Hotspot 2.0 r2 OSEN.
+    AUTH_TYPE_OSEN = 5;
+    // IEEE 802.11r Fast BSS Transition with PSK authentication.
+    AUTH_TYPE_FT_PSK = 6;
+    // IEEE 802.11r Fast BSS Transition with EAP authentication.
+    AUTH_TYPE_FT_EAP = 7;
+    // Simultaneous Authentication of Equals.
+    AUTH_TYPE_SAE = 8;
+    // Opportunistic Wireless Encryption.
+    AUTH_TYPE_OWE = 9;
+    // SUITE_B_192 192 bit level
+    AUTH_TYPE_SUITE_B_192 = 10;
+    // WPA pre-shared key with stronger SHA256-based algorithms.
+    AUTH_TYPE_WPA_PSK_SHA256 = 11;
+    // WPA using EAP authentication with stronger SHA256-based algorithms.
+    AUTH_TYPE_WPA_EAP_SHA256 = 12;
+    // WAPI pre-shared key.
+    AUTH_TYPE_WAPI_PSK = 13;
+    // WAPI certificate to be specified.
+    AUTH_TYPE_WAPI_CERT = 14;
+    // IEEE 802.11ai FILS SK with SHA256.
+    AUTH_TYPE_FILS_SHA256 = 15;
+    // IEEE 802.11ai FILS SK with SHA384.
+    AUTH_TYPE_FILS_SHA384 = 16;
+}
+
+/**
+ * Bucketed wifi band.
+ */
+enum WifiBandBucket {
+    BAND_UNKNOWN = 0;
+
+    // All of 2.4GHz band
+    BAND_2G = 1;
+    // Frequencies in the range of [5150, 5250) GHz
+    BAND_5G_LOW = 2;
+    // Frequencies in the range of [5250, 5725) GHz
+    BAND_5G_MIDDLE = 3;
+    // Frequencies in the range of [5725, 5850) GHz
+    BAND_5G_HIGH = 4;
+    // Frequencies in the range of [5925, 6425) GHz
+    BAND_6G_LOW = 5;
+    // Frequencies in the range of [6425, 6875) GHz
+    BAND_6G_MIDDLE = 6;
+    // Frequencies in the range of [6875, 7125) GHz
+    BAND_6G_HIGH = 7;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cdcb24b..560e3c1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4691,6 +4691,12 @@
     <permission android:name="android.permission.HANDLE_CAR_MODE_CHANGES"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows the holder to send category_car notifications.
+        @hide -->
+    <permission
+        android:name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
@@ -5496,6 +5502,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service
                 android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
                 android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index b6f6627..d0d7eca 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -272,9 +272,9 @@
     <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"وضع صامت"</string>
     <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"الصوت متوقف"</string>
     <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"الصوت قيد التفعيل"</string>
-    <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطائرة"</string>
-    <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطائرة قيد التفعيل"</string>
-    <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطائرة متوقف"</string>
+    <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"وضع الطيران"</string>
+    <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"وضع الطيران قيد التفعيل"</string>
+    <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"وضع الطيران متوقف"</string>
     <string name="global_action_settings" msgid="4671878836947494217">"الإعدادات"</string>
     <string name="global_action_assist" msgid="2517047220311505805">"مساعدة"</string>
     <string name="global_action_voice_assist" msgid="6655788068555086695">"المساعد الصوتي"</string>
@@ -2156,7 +2156,7 @@
     <string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"جدول بيانات: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
     <string name="mime_type_presentation" msgid="1145384236788242075">"عرض تقديمي"</string>
     <string name="mime_type_presentation_ext" msgid="8761049335564371468">"عرض تقديمي: <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
-    <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطائرة\"."</string>
+    <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"سيظل البلوتوث مفعَّلاً أثناء استخدام \"وضع الطيران\"."</string>
     <string name="car_loading_profile" msgid="8219978381196748070">"جارٍ التحميل"</string>
     <plurals name="file_count" formatted="false" msgid="7063513834724389247">
       <item quantity="zero"><xliff:g id="FILE_NAME_2">%s</xliff:g> و<xliff:g id="COUNT_3">%d</xliff:g> ملف</item>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index feef587..755ee47 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -973,7 +973,7 @@
     <string name="save_password_remember" msgid="6490888932657708341">"Zapamti"</string>
     <string name="save_password_never" msgid="6776808375903410659">"Nikad"</string>
     <string name="open_permission_deny" msgid="5136793905306987251">"Nemate odobrenje za otvaranje ove stranice."</string>
-    <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međuspremnik."</string>
+    <string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međumemoriju."</string>
     <string name="copied" msgid="4675902854553014676">"Kopirano"</string>
     <string name="more_item_label" msgid="7419249600215749115">"Više"</string>
     <string name="prepend_shortcut_label" msgid="1743716737502867951">"Meni+"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 84f3243..2308b5a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -423,7 +423,7 @@
     <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Esta app puede agregar, quitar o cambiar eventos del calendario en tu teléfono. Puede enviar mensajes que parecen proceder de propietarios del calendario o cambiar eventos sin notificarlos."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"acceder a comandos adicionales del proveedor del lugar"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string>
-    <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación exacta solo en primer plano"</string>
+    <string name="permlab_accessFineLocation" msgid="6426318438195622966">"acceder a la ubicación precisa solo en primer plano"</string>
     <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Mientras la usas, esta app puede obtener tu ubicación exacta mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados. Es posible que esto aumente el uso de batería."</string>
     <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"acceder a la ubicación aproximada solo en primer plano"</string>
     <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Mientras la usas, esta app puede obtener tu ubicación aproximada mediante los Servicios de ubicación, siempre y cuando el dispositivo los tenga activados."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9062435..3b44295 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -309,7 +309,7 @@
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"hanganyag rögzítése"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Testmozgás"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"hozzáférés a testmozgási adatokhoz"</string>
-    <string name="permgrouplab_camera" msgid="9090413408963547706">"Fényképezőgép"</string>
+    <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"fotók és videók készítése"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"Hívásnaplók"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"hívásnapló olvasása és írása"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 671863d..162b4d3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1320,7 +1320,7 @@
     <string name="console_running_notification_title" msgid="6087888939261635904">"Сериялык консоль иштетилди"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"Майнаптуулугуна таасири тиет. Аны өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string>
     <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB портунда суюктук же урандылар бар"</string>
-    <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string>
+    <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт автоматтык түрдө өчтү. Кененирээк маалымат алуу үчүн, таптап коюңуз."</string>
     <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"USB портун колдонууга болот"</string>
     <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Телефон суюктук менен урандыларды аныктаган жок."</string>
     <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Мүчүлүштүк тууралуу кабар алынууда…"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f63e647..95f7439 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1279,7 +1279,7 @@
     <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nooit toestaan"</string>
     <string name="sim_removed_title" msgid="5387212933992546283">"Simkaart verwijderd"</string>
     <string name="sim_removed_message" msgid="9051174064474904617">"Het mobiele netwerk is niet beschikbaar totdat u het apparaat opnieuw start met een geldige simkaart."</string>
-    <string name="sim_done_button" msgid="6464250841528410598">"Gereed"</string>
+    <string name="sim_done_button" msgid="6464250841528410598">"Klaar"</string>
     <string name="sim_added_title" msgid="7930779986759414595">"Simkaart aangesloten"</string>
     <string name="sim_added_message" msgid="6602906609509958680">"Start je apparaat opnieuw voor toegang tot het mobiele netwerk."</string>
     <string name="sim_restart_button" msgid="8481803851341190038">"Opnieuw starten"</string>
@@ -1292,7 +1292,7 @@
     <string name="time_picker_dialog_title" msgid="9053376764985220821">"Tijd instellen"</string>
     <string name="date_picker_dialog_title" msgid="5030520449243071926">"Datum instellen"</string>
     <string name="date_time_set" msgid="4603445265164486816">"Instellen"</string>
-    <string name="date_time_done" msgid="8363155889402873463">"Gereed"</string>
+    <string name="date_time_done" msgid="8363155889402873463">"Klaar"</string>
     <string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NIEUW: "</font></string>
     <string name="perms_description_app" msgid="2747752389870161996">"Geleverd door <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="no_permissions" msgid="5729199278862516390">"Geen rechten nodig"</string>
@@ -1379,7 +1379,7 @@
     <string name="ext_media_status_removed" msgid="241223931135751691">"Verwijderd"</string>
     <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Uitgeworpen"</string>
     <string name="ext_media_status_checking" msgid="159013362442090347">"Controleren…"</string>
-    <string name="ext_media_status_mounted" msgid="3459448555811203459">"Gereed"</string>
+    <string name="ext_media_status_mounted" msgid="3459448555811203459">"Klaar"</string>
     <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Alleen lezen"</string>
     <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Onveilig verwijderd"</string>
     <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Beschadigd"</string>
@@ -1404,7 +1404,7 @@
     <string name="ime_action_search" msgid="4501435960587287668">"Zoeken"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"Verzenden"</string>
     <string name="ime_action_next" msgid="4169702997635728543">"Volgende"</string>
-    <string name="ime_action_done" msgid="6299921014822891569">"Gereed"</string>
+    <string name="ime_action_done" msgid="6299921014822891569">"Klaar"</string>
     <string name="ime_action_previous" msgid="6548799326860401611">"Vorige"</string>
     <string name="ime_action_default" msgid="8265027027659800121">"Uitvoeren"</string>
     <string name="dial_number_using" msgid="6060769078933953531">"Nummer bellen\nmet <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -1451,7 +1451,7 @@
       <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> van <xliff:g id="TOTAL">%d</xliff:g></item>
       <item quantity="one">1 overeenkomst</item>
     </plurals>
-    <string name="action_mode_done" msgid="2536182504764803222">"Gereed"</string>
+    <string name="action_mode_done" msgid="2536182504764803222">"Klaar"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Gedeelde opslag wissen…"</string>
     <string name="share" msgid="4157615043345227321">"Delen"</string>
     <string name="find" msgid="5015737188624767706">"Vinden"</string>
@@ -1492,7 +1492,7 @@
     <string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Annuleren"</string>
     <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Delete"</string>
-    <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Gereed"</string>
+    <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Klaar"</string>
     <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Modus wijzigen"</string>
     <string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string>
@@ -1645,7 +1645,7 @@
     <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string>
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgeschakeld"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string>
-    <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gereed"</string>
+    <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
     <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Sneltoets uitschakelen"</string>
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string>
@@ -1774,7 +1774,7 @@
     <string name="immersive_cling_title" msgid="2307034298721541791">"Volledig scherm wordt weergegeven"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string>
-    <string name="done_label" msgid="7283767013231718521">"Gereed"</string>
+    <string name="done_label" msgid="7283767013231718521">"Klaar"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Ronde schuifregelaar voor uren"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Ronde schuifregelaar voor minuten"</string>
     <string name="select_hours" msgid="5982889657313147347">"Uren selecteren"</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index e17c312..6ae6faa 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -50,6 +50,7 @@
     <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
     <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
     <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
+    <uses-permission android:name="android.permission.DEVICE_POWER"/>
     <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 0490678..88faa0a 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -574,16 +574,16 @@
 
         InstallParams(String outFileName, int rawResId) throws PackageParserException {
             this.pkg = getParsedPackage(outFileName, rawResId);
-            this.packageURI = Uri.fromFile(new File(pkg.getCodePath()));
+            this.packageURI = Uri.fromFile(new File(pkg.getPath()));
         }
 
         InstallParams(ParsingPackage pkg) {
-            this.packageURI = Uri.fromFile(new File(pkg.getCodePath()));
+            this.packageURI = Uri.fromFile(new File(pkg.getPath()));
             this.pkg = pkg;
         }
 
         long getApkSize() {
-            File file = new File(pkg.getCodePath());
+            File file = new File(pkg.getPath());
             return file.length();
         }
     }
@@ -1003,7 +1003,7 @@
         try {
             cleanUpInstall(ip.pkg.getPackageName());
         } finally {
-            File outFile = new File(ip.pkg.getCodePath());
+            File outFile = new File(ip.pkg.getPath());
             if (outFile != null && outFile.exists()) {
                 outFile.delete();
             }
diff --git a/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
index fbf75df..a01459f 100644
--- a/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
+++ b/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
@@ -146,7 +146,7 @@
                         when(a.getTargetSdkVersion()).thenReturn(info.targetSdkVersion);
                         when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
                         when(a.getOverlayPriority()).thenReturn(info.priority);
-                        when(a.getBaseCodePath()).thenReturn(info.path.getPath());
+                        when(a.getBaseApkPath()).thenReturn(info.path.getPath());
                         f.accept(a, !info.path.getPath().contains("data/overlay"));
                     }
                     return null;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
new file mode 100644
index 0000000..e870d60
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.os.ConditionVariable;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import com.google.android.collect.Sets;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryInputSuspendTest {
+
+    public static final Set<String> SUPPORTED_DEVICES = Sets.newArraySet(
+            "blueline",
+            "crosshatch",
+            "coral"
+    );
+
+    private ChargerStateMonitor mChargerStateMonitor;
+
+    @Before
+    public void verifyCharging() {
+        if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) {
+            return;
+        }
+
+        mChargerStateMonitor = new ChargerStateMonitor();
+
+        assertTrue("Device must be connected to USB", mChargerStateMonitor.isCharging());
+    }
+
+    @Test
+    public void testSuspendInput() {
+        if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) {
+            return;
+        }
+
+        SystemUtil.runShellCommand("dumpsys battery suspend_input");
+
+        mChargerStateMonitor.waitForChargerState(/* isPluggedIn */false);
+    }
+
+    @After
+    public void reenableCharging() {
+        if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) {
+            return;
+        }
+
+        mChargerStateMonitor.reset();
+
+        SystemUtil.runShellCommand("dumpsys battery reset");
+
+        mChargerStateMonitor.waitForChargerState(/* isPluggedIn */true);
+    }
+
+    private static class ChargerStateMonitor {
+        private final Intent mBatteryMonitor;
+        private final ConditionVariable mReady = new ConditionVariable();
+        private boolean mExpectedChargingState;
+
+        ChargerStateMonitor() {
+            Context context = InstrumentationRegistry.getInstrumentation().getContext();
+            mBatteryMonitor = context.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (isCharging(intent) == mExpectedChargingState) {
+                        mReady.open();
+                    }
+                }
+            }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        }
+
+        public boolean isCharging() {
+            return isCharging(mBatteryMonitor);
+        }
+
+        private boolean isCharging(Intent batteryMonitor) {
+            return batteryMonitor.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
+        }
+
+        void waitForChargerState(boolean isPluggedIn) {
+            mExpectedChargingState = isPluggedIn;
+
+            boolean charging = isCharging();
+            if (charging == mExpectedChargingState) {
+                return;
+            }
+
+            boolean success = mReady.block(100000);
+            assertTrue("Timed out waiting for charging state to change", success);
+        }
+
+        void reset() {
+            mReady.close();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index 61d20df..3b27f18 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -18,6 +18,7 @@
 
 import android.os.BatteryStats;
 import android.os.Parcel;
+import android.os.SystemClock;
 
 import androidx.test.filters.SmallTest;
 
@@ -36,9 +37,9 @@
         timer.onTimeStarted(100, 100, 100);
 
         // First update is absorbed.
-        timer.update(10, 1);
+        timer.update(10, 1, SystemClock.elapsedRealtime() * 1000);
 
-        timer.update(20, 2);
+        timer.update(20, 2, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(1, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(10, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
@@ -62,7 +63,7 @@
                 timeBase);
 
         // First once is absorbed.
-        timer.update(10, 1);
+        timer.update(10, 1, SystemClock.elapsedRealtime() * 1000);
 
         timer.add(10, 1);
 
@@ -71,7 +72,7 @@
 
         // This is less than we currently have, so we will end the sample. Time isn't running, so
         // nothing should happen.
-        timer.update(0, 0);
+        timer.update(0, 0, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(0, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
@@ -86,7 +87,7 @@
 
         // This is less than we currently have, so we should end our sample and continue with the
         // entire amount updated here.
-        timer.update(50, 5);
+        timer.update(50, 5, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(150, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(15, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
@@ -106,7 +107,7 @@
 
         // This should be absorbed because it is our first update and we don't know what
         // was being counted before.
-        timer.update(10, 1);
+        timer.update(10, 1, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(0, timer.getTotalTimeLocked(10, BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
@@ -115,7 +116,7 @@
         timer.onTimeStarted(100, 100, 100);
 
         // This should be absorbed.
-        timer.update(10, 1);
+        timer.update(10, 1, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(0, timer.getTotalTimeLocked(100, BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
@@ -206,13 +207,13 @@
 
         // Now, just like with a fresh timer, the first update should be absorbed to account for
         // data being collected when we weren't recording.
-        unparceledOnBatteryTimer.update(10, 10);
+        unparceledOnBatteryTimer.update(10, 10, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(10, unparceledOnBatteryTimer.getTotalTimeLocked(0,
                 BatteryStats.STATS_SINCE_CHARGED));
         assertEquals(1, unparceledOnBatteryTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
 
-        unparceledOffBatteryTimer.update(10, 10);
+        unparceledOffBatteryTimer.update(10, 10, SystemClock.elapsedRealtime() * 1000);
 
         assertEquals(10, unparceledOffBatteryTimer.getTotalTimeLocked(0,
                 BatteryStats.STATS_SINCE_CHARGED));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
index df549c5..80e066c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsServTest.java
@@ -36,22 +36,22 @@
         }
 
         void populate() {
-            mStartTime = 1010;
-            mRunningSince = 2021;
+            mStartTimeMs = 1010;
+            mRunningSinceMs = 2021;
             mRunning = true;
             mStarts = 4042;
-            mLaunchedTime = 5053;
-            mLaunchedSince = 6064;
+            mLaunchedTimeMs = 5053;
+            mLaunchedSinceMs = 6064;
             mLaunched = true;
             mLaunches = 8085;
         }
 
         long getStartTime() {
-            return mStartTime;
+            return mStartTimeMs;
         }
 
         long getRunningSince() {
-            return mRunningSince;
+            return mRunningSinceMs;
         }
 
         void setRunning(boolean val) {
@@ -67,11 +67,11 @@
         }
 
         long getLaunchedTime() {
-            return mLaunchedTime;
+            return mLaunchedTimeMs;
         }
 
         long getLaunchedSince() {
-            return mLaunchedSince;
+            return mLaunchedSinceMs;
         }
 
         void setLaunched(boolean val) {
@@ -173,7 +173,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
 
@@ -202,7 +207,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
 
@@ -229,7 +239,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
         TestServ serv = new TestServ(bsi);
@@ -259,7 +274,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
         TestServ serv = new TestServ(bsi);
@@ -316,7 +336,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
         TestServ serv = new TestServ(bsi);
@@ -345,7 +370,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
         TestServ serv = new TestServ(bsi);
@@ -374,7 +404,12 @@
         MockBatteryStatsImpl bsi = new MockBatteryStatsImpl() {
             @Override
             public long getBatteryUptimeLocked() {
-                return 777777L;
+                return 777777L * 1000; // microseconds
+            }
+
+            @Override
+            public long getBatteryUptimeLocked(long uptimeMs) {
+                return 777777L * 1000; // microseconds
             }
         };
         TestServ serv = new TestServ(bsi);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
index e5441c0..f016e75a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimeBaseTest.java
@@ -41,29 +41,29 @@
         public void populate(long uptime, long realtime, boolean running, long pastUptime,
                 long uptimeStart, long pastRealtime, long realtimeStart,
                 long unpluggedUptime, long unpluggedRealtime) {
-            mUptime = uptime;
-            mRealtime = realtime;
+            mUptimeUs = uptime;
+            mRealtimeUs = realtime;
             mRunning = running;
-            mPastUptime = pastUptime;
-            mUptimeStart = uptimeStart;
-            mPastRealtime = pastRealtime;
-            mRealtimeStart = realtimeStart;
-            mUnpluggedUptime = unpluggedUptime;
-            mUnpluggedRealtime = unpluggedRealtime;
+            mPastUptimeUs = pastUptime;
+            mUptimeStartUs = uptimeStart;
+            mPastRealtimeUs = pastRealtime;
+            mRealtimeStartUs = realtimeStart;
+            mUnpluggedUptimeUs = unpluggedUptime;
+            mUnpluggedRealtimeUs = unpluggedRealtime;
         }
 
         public void verify(long uptime, long realtime, boolean running, long pastUptime,
                 long uptimeStart, long pastRealtime, long realtimeStart,
                 long unpluggedUptime, long unpluggedRealtime) {
-            Assert.assertEquals(uptime, mUptime);
-            Assert.assertEquals(realtime, mRealtime);
+            Assert.assertEquals(uptime, mUptimeUs);
+            Assert.assertEquals(realtime, mRealtimeUs);
             Assert.assertEquals(running, mRunning);
-            Assert.assertEquals(pastUptime, mPastUptime);
-            Assert.assertEquals(uptimeStart, mUptimeStart);
-            Assert.assertEquals(pastRealtime, mPastRealtime);
-            Assert.assertEquals(realtimeStart, mRealtimeStart);
-            Assert.assertEquals(unpluggedUptime, mUnpluggedUptime);
-            Assert.assertEquals(unpluggedRealtime, mUnpluggedRealtime);
+            Assert.assertEquals(pastUptime, mPastUptimeUs);
+            Assert.assertEquals(uptimeStart, mUptimeStartUs);
+            Assert.assertEquals(pastRealtime, mPastRealtimeUs);
+            Assert.assertEquals(realtimeStart, mRealtimeStartUs);
+            Assert.assertEquals(unpluggedUptime, mUnpluggedUptimeUs);
+            Assert.assertEquals(unpluggedRealtime, mUnpluggedRealtimeUs);
         }
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
index 40e3a5f..11a01b3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
@@ -49,7 +49,8 @@
             super(clocks, type, timeBase);
         }
 
-        protected long computeRunTimeLocked(long curBatteryRealtime) {
+        @Override
+        protected long computeRunTimeLocked(long curBatteryRealtime, long elapsedRealtimeUs) {
             lastComputeRunTimeRealtime = curBatteryRealtime;
             return nextComputeRunTime;
         }
@@ -67,19 +68,19 @@
         }
 
         public long getTotalTime() {
-            return mTotalTime;
+            return mTotalTimeUs;
         }
 
         public void setTotalTime(long val) {
-            mTotalTime = val;
+            mTotalTimeUs = val;
         }
 
         public long getTimeBeforeMark() {
-            return mTimeBeforeMark;
+            return mTimeBeforeMarkUs;
         }
 
         public void setTimeBeforeMark(long val) {
-            mTimeBeforeMark = val;
+            mTimeBeforeMarkUs = val;
         }
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
index 359bd5e..f1cd89b 100644
--- a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
@@ -16,8 +16,16 @@
 
 package com.android.internal.util;
 
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+import static com.android.internal.util.HexDump.toHexString;
+
 import junit.framework.TestCase;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Random;
+
 public final class HexDumpTest extends TestCase {
     public void testBytesToHexString() {
         assertEquals("abcdef", HexDump.toHexString(
@@ -25,7 +33,143 @@
         assertEquals("ABCDEF", HexDump.toHexString(
                 new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
     }
-    public void testNullArray() {
-        assertEquals("(null)", HexDump.toHexString(null));
+
+    public void testNullByteArray() {
+        assertThrows(
+                NullPointerException.class,
+                () -> HexDump.toHexString(null));
     }
+
+    public void testBytesToHexString_allByteValues() {
+        byte[] bytes = new byte[256];
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] = (byte) (i % 256);
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (char firstChar : "0123456789ABCDEF".toCharArray()) {
+            for (char secondChar : "0123456789ABCDEF".toCharArray()) {
+                sb.append(firstChar).append(secondChar);
+            }
+        }
+        String expected = sb.toString();
+
+        assertEquals(expected, HexDump.toHexString(bytes));
+    }
+
+    public void testRoundTrip_fromBytes() {
+        Random deterministicRandom = new Random(31337); // arbitrary but deterministic
+        for (int length = 0; length < 100; length++) {
+            byte[] bytes = new byte[length];
+            deterministicRandom.nextBytes(bytes);
+            byte[] reconstruction = hexStringToByteArray(toHexString(bytes));
+
+            assertBytesEqual(bytes, reconstruction);
+        }
+    }
+
+    public void testRoundTrip_fromString() {
+        String hexString = "0123456789ABCDEF72f9a3438934c378d34f32a8b932";
+        for (int length = 0; length < hexString.length(); length += 2) {
+            String original = hexString.substring(0, length);
+            String reconstruction = toHexString(hexStringToByteArray(original));
+            assertEquals(original.toUpperCase(), reconstruction);
+        }
+    }
+
+    public void testToHexString_offsetLength() {
+        byte[] bytes = new byte[32];
+        for (int i = 0; i < 16; i++) {
+            bytes[i] = (byte) i;
+            bytes[16 + i] = (byte) (16 * i);
+        }
+        String expected = "000102030405060708090A0B0C0D0E0F00102030405060708090A0B0C0D0E0F0";
+        for (int offset = 0; offset < bytes.length; offset++) {
+            for (int len = 0; len < (bytes.length - offset); len++) {
+
+                byte[] subBytes = new byte[len];
+                System.arraycopy(bytes, offset, subBytes, 0, len);
+
+                String actual = toHexString(bytes, offset, len);
+                assertEquals(expected.substring(2 * offset, 2 * offset + 2 * len), actual);
+                assertEquals(toHexString(subBytes), actual);
+            }
+        }
+    }
+
+    public void testToHexString_case() {
+        byte[] bytes = new byte[32];
+        for (int i = 0; i < 16; i++) {
+            bytes[i] = (byte) i;
+            bytes[16 + i] = (byte) (16 * i);
+        }
+
+        String expected = "000102030405060708090A0B0C0D0E0F00102030405060708090A0B0C0D0E0F0";
+
+        assertEquals(expected.toUpperCase(), toHexString(bytes, true));
+        assertEquals(expected.toLowerCase(), toHexString(bytes, false));
+
+        // default is uppercase
+        assertEquals(expected.toUpperCase(), toHexString(bytes));
+    }
+
+    public void testHexStringToByteArray_empty() {
+        assertBytesEqual(new byte[0], HexDump.hexStringToByteArray(""));
+    }
+
+    public void testHexStringToByteArray_null() {
+        assertThrows(
+                NullPointerException.class,
+                () -> HexDump.hexStringToByteArray((String) null));
+    }
+
+    public void testHexStringToByteArray_invalidCharacters() {
+        // IllegalArgumentException would probably have been better than RuntimeException, but it
+        // might be too late to change now.
+        assertThrows(
+                RuntimeException.class,
+                () -> HexDump.hexStringToByteArray("GG"));
+        assertThrows(
+                RuntimeException.class,
+                () -> HexDump.hexStringToByteArray("\0\0"));
+        assertThrows(
+                RuntimeException.class,
+                () -> HexDump.hexStringToByteArray("abcdefgh"));
+    }
+
+    public void testHexStringToByteArray_oddLength() {
+        // IllegalArgumentException would probably have been better than
+        // StringIndexOutOfBoundsException, but it might be too late to change now.
+        assertThrows(
+                StringIndexOutOfBoundsException.class,
+                () -> HexDump.hexStringToByteArray("A"));
+        assertThrows(
+                StringIndexOutOfBoundsException.class,
+                () -> HexDump.hexStringToByteArray("123"));
+        assertThrows(
+                StringIndexOutOfBoundsException.class,
+                () -> HexDump.hexStringToByteArray("ABCDE"));
+    }
+
+    private static void assertBytesEqual(byte[] expected, byte[] actual) {
+        if (!Arrays.equals(expected, actual)) {
+            fail("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual));
+        }
+    }
+
+    private static void assertThrows(Class<? extends RuntimeException> clazz, Runnable runnable) {
+        try {
+            runnable.run();
+            fail();
+        } catch (RuntimeException expected) {
+            assertEquals(toStrackTrace(expected), clazz, expected.getClass());
+        }
+    }
+
+    private static String toStrackTrace(Throwable throwable) {
+        StringWriter stringWriter = new StringWriter();
+        throwable.printStackTrace(new PrintWriter(stringWriter));
+        return stringWriter.toString();
+    }
+
 }
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 7292e07..59aa45e 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -40,6 +40,7 @@
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.READ_LOGS"/>
         <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
         <!-- use for CarServiceTest -->
         <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index a2a2216..e693198 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1393,6 +1393,12 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/BlackFrame.java"
     },
+    "155482615": {
+      "message": "Focus requested for window=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/InputMonitor.java"
+    },
     "174572959": {
       "message": "DisplayArea info changed name=%s",
       "level": "VERBOSE",
@@ -2635,6 +2641,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
     },
+    "2081291430": {
+      "message": "Focus not requested for window=%s because it has no surface",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/InputMonitor.java"
+    },
     "2083556954": {
       "message": "Set mOrientationChanging of %s",
       "level": "VERBOSE",
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 4f4364f..7fbbb61 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -24,14 +24,14 @@
     static_libs: ["window-sidecar"],
     installable: true,
     sdk_version: "core_platform",
-    vendor: true,
+    system_ext_specific: true,
     libs: ["framework", "androidx.annotation_annotation",],
     required: ["androidx.window.sidecar.xml",],
 }
 
 prebuilt_etc {
     name: "androidx.window.sidecar.xml",
-    vendor: true,
+    system_ext_specific: true,
     sub_dir: "permissions",
     src: "androidx.window.sidecar.xml",
     filename_from_src: true,
diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
index f88a5f4..359e69f 100644
--- a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
+++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
@@ -17,5 +17,5 @@
 <permissions>
     <library
         name="androidx.window.sidecar"
-        file="/vendor/framework/androidx.window.sidecar.jar"/>
+        file="/system_ext/framework/androidx.window.sidecar.jar"/>
 </permissions>
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 16b87c4..1591b06 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -105,6 +105,7 @@
     static_libs: [
         "protolog-lib",
         "WindowManager-Shell-proto",
+        "androidx.appcompat_appcompat",
     ],
     manifest: "AndroidManifest.xml",
 }
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png b/libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png
rename to libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/xml/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
similarity index 100%
rename from packages/SystemUI/res/xml/one_handed_tutorial.xml
rename to libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 7242793..a13e98c 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -1,6 +1,12 @@
 {
   "version": "1.0.0",
   "messages": {
+    "-1501874464": {
+      "message": "Fullscreen Task Appeared: #%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
+    },
     "-1340279385": {
       "message": "Remove listener=%s",
       "level": "VERBOSE",
@@ -31,6 +37,12 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
+    "564235578": {
+      "message": "Fullscreen Task Vanished: #%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
+    },
     "980952660": {
       "message": "Task root back pressed taskId=%d",
       "level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 39efd07..63b0f6f 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -29,4 +29,7 @@
 
     <!-- Animation duration when using long press on recents to dock -->
     <integer name="long_press_dock_anim_duration">250</integer>
+
+    <!-- Allow one handed to enable round corner -->
+    <bool name="config_one_handed_enable_round_corner">true</bool>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index ce69028..7fb641a 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -62,4 +62,8 @@
 
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
+
+    <!-- One-Handed Mode -->
+    <!-- Threshold for dragging distance to enable one-handed mode -->
+    <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index cad9247..b6668fb 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -88,4 +88,9 @@
     <string name="accessibility_action_divider_top_30">Top 30%</string>
     <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] -->
     <string name="accessibility_action_divider_bottom_full">Bottom full screen</string>
+
+    <!-- One-Handed Tutorial title [CHAR LIMIT=60] -->
+    <string name="one_handed_tutorial_title">Using one-handed mode</string>
+    <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
+    <string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
new file mode 100644
index 0000000..d0051db
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -0,0 +1,69 @@
+/*
+ * 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.wm.shell;
+
+import android.app.ActivityManager;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
+    private static final String TAG = "FullscreenTaskOrg";
+
+    private final TransactionPool mTransactionPool;
+
+    private final ArraySet<Integer> mTasks = new ArraySet<>();
+
+    FullscreenTaskListener(TransactionPool transactionPool) {
+        mTransactionPool = transactionPool;
+    }
+
+    @Override
+    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+        synchronized (mTasks) {
+            if (mTasks.contains(taskInfo.taskId)) {
+                throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
+            }
+            mTasks.add(taskInfo.taskId);
+            final SurfaceControl.Transaction t = mTransactionPool.acquire();
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
+                    taskInfo.taskId);
+            t.show(leash);
+            t.apply();
+        }
+    }
+
+    @Override
+    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        synchronized (mTasks) {
+            if (!mTasks.remove(taskInfo.taskId)) {
+                Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+                return;
+            }
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
+                    taskInfo.taskId);
+        }
+    }
+
+    @Override
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f9ba695c..2d82fb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
 import android.app.ActivityManager.RunningTaskInfo;
 import android.util.Log;
 import android.util.Pair;
@@ -26,6 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.util.ArrayList;
@@ -56,13 +59,16 @@
     // require us to report to both old and new listeners)
     private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
 
-    public ShellTaskOrganizer() {
+    public ShellTaskOrganizer(TransactionPool transactionPool) {
         super();
+        addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
     }
 
     @VisibleForTesting
-    ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController) {
+    ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
+                       TransactionPool transactionPool) {
         super(taskOrganizerController);
+        addListener(new FullscreenTaskListener(transactionPool), WINDOWING_MODE_FULLSCREEN);
     }
 
     /**
@@ -125,6 +131,7 @@
         Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
         int winMode = getWindowingMode(taskInfo);
         int prevWinMode = getWindowingMode(data.first);
+        mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second));
         if (prevWinMode != -1 && prevWinMode != winMode) {
             // TODO: We currently send vanished/appeared as the task moves between win modes, but
             //       we should consider adding a different mode-changed callback
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 8abe9ee..b4620e27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -32,7 +31,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
-import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IScrollCaptureController;
 import android.view.IWindow;
@@ -47,6 +45,7 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -274,22 +273,20 @@
         @Override
         public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
                 int requestedWidth, int requestedHeight, int viewVisibility, int flags,
-                long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-                Rect outVisibleInsets, Rect outStableInsets,
-                DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
+                long frameNumber, ClientWindowFrames outFrames,
+                MergedConfiguration mergedConfiguration,
                 SurfaceControl outSurfaceControl, InsetsState outInsetsState,
                 InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
                 SurfaceControl outBLASTSurfaceControl) {
             int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
-                    viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
-                    outContentInsets, outVisibleInsets, outStableInsets,
-                    cutout, mergedConfiguration, outSurfaceControl, outInsetsState,
+                    viewVisibility, flags, frameNumber, outFrames,
+                    mergedConfiguration, outSurfaceControl, outInsetsState,
                     outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
             if (res != 0) {
                 return res;
             }
             DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId);
-            outStableInsets.set(dl.stableInsets());
+            outFrames.stableInsets.set(dl.stableInsets());
             return 0;
         }
 
@@ -314,10 +311,9 @@
         ContainerWindow() {}
 
         @Override
-        public void resized(Rect frame, Rect contentInsets, Rect visibleInsets, Rect stableInsets,
-                boolean reportDraw, MergedConfiguration newMergedConfiguration, Rect backDropFrame,
-                boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
-                DisplayCutout.ParcelableWrapper displayCutout) {}
+        public void resized(ClientWindowFrames frames, boolean reportDraw,
+                MergedConfiguration newMergedConfiguration, boolean forceLayout,
+                boolean alwaysConsumeSystemBars, int displayId) {}
 
         @Override
         public void locationInParentDisplayChanged(Point offset) {}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHanded.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index b7c6262..9c78fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
 
 import java.io.PrintWriter;
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
index 264ace7..6749f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.view.SurfaceControl;
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 9be1b5a..9639096 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 90adf83..c84b478 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.os.UserHandle.USER_CURRENT;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -35,9 +35,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
 
 import java.io.PrintWriter;
 
@@ -390,7 +390,7 @@
         }
     }
 
-    @androidx.annotation.VisibleForTesting
+    @VisibleForTesting
     private void setEnabledGesturalOverlay(boolean enabled) {
         try {
             mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index ec40bad..9954618 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
-import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
+import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
+import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
 
 import android.content.Context;
 import android.graphics.Point;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedEvents.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedEvents.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
index 327ed67..79ddd2b 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedEvents.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index 4a493ba..3b1e6cb 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -38,7 +38,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 
@@ -210,7 +210,7 @@
                     displaySize.y);
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "onehanded-gesture-offset", DEFAULT_DISPLAY);
-            mInputEventReceiver = new SysUiInputEventReceiver(
+            mInputEventReceiver = new EventReceiver(
                     mInputMonitor.getInputChannel(), Looper.getMainLooper());
         }
     }
@@ -227,8 +227,8 @@
         mRotation = toRotation;
     }
 
-    private class SysUiInputEventReceiver extends InputEventReceiver {
-        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+    private class EventReceiver extends InputEventReceiver {
+        EventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 0598f32..4d66f29 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.annotation.IntDef;
 import android.content.ContentResolver;
@@ -22,8 +22,6 @@
 import android.net.Uri;
 import android.provider.Settings;
 
-import com.android.systemui.dagger.SysUISingleton;
-
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -31,7 +29,6 @@
 /**
  * APIs for querying or updating one handed settings .
  */
-@SysUISingleton
 public final class OneHandedSettingsUtil {
     private static final String TAG = "OneHandedSettingsUtil";
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
index bc4a9b4..e7010db 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
 /**
  * Abstracts the common operations on {@link SurfaceControl.Transaction} for OneHanded transition.
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedThread.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
index b7b814a..24d33ed 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedThread.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.os.Handler;
 import android.os.HandlerThread;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
index 21329ea..9c97cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
 
 import android.os.Handler;
 import android.os.Looper;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
index 3d28a42..721382d 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -127,7 +127,7 @@
         if (mIsEnabled) {
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "onehanded-touch", DEFAULT_DISPLAY);
-            mInputEventReceiver = new SysUiInputEventReceiver(
+            mInputEventReceiver = new EventReceiver(
                     mInputMonitor.getInputChannel(), Looper.getMainLooper());
         }
     }
@@ -150,8 +150,8 @@
         pw.println(mLastUpdatedBounds);
     }
 
-    private class SysUiInputEventReceiver extends InputEventReceiver {
-        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+    private class EventReceiver extends InputEventReceiver {
+        EventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTransitionCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTransitionCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
index 75eb0eb..3af7c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTransitionCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.graphics.Rect;
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index beccf3d..b15b515 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -33,7 +33,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
 import java.io.PrintWriter;
 
@@ -79,7 +79,7 @@
         mTargetViewContainer.setClipChildren(false);
         mTutorialAreaHeight = Math.round(mDisplaySize.y
                 * (SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f));
-        mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null);
+        mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
         mTargetViewContainer.addView(mTutorialView);
         mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
                 Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
index 015707e..25827cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
@@ -194,7 +194,7 @@
      */
     boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
         // Set launchtile first so that any stack created after
-        // getAllStackInfos and before reparent (even if unlikely) are placed
+        // getAllRootTaskInfos and before reparent (even if unlikely) are placed
         // correctly.
         mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token);
         List<ActivityManager.RunningTaskInfo> rootTasks =
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 5879022..d7afa0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -18,7 +18,7 @@
     name: "WMShellFlickerTests",
     srcs: ["src/**/*.java", "src/**/*.kt"],
     manifest: "AndroidManifest.xml",
-    test_config: "AndroidTest.xml",
+    test_config: "AndroidTestPhysicalDevices.xml",
     platform_apis: true,
     certificate: "platform",
     test_suites: ["device-tests"],
@@ -32,3 +32,21 @@
         "launcher-aosp-tapl"
     ],
 }
+
+android_test {
+    name: "WMShellFlickerTestsVirtual",
+    srcs: ["src/**/*.java", "src/**/*.kt"],
+    manifest: "AndroidManifest.xml",
+    test_config: "AndroidTestVirtualDevices.xml",
+    platform_apis: true,
+    certificate: "platform",
+    libs: ["android.test.runner"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "flickerlib",
+        "truth-prebuilt",
+        "app-helpers-core",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl"
+    ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
similarity index 95%
copy from libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
copy to libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
index 526fc50..9dd9f42 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
@@ -27,6 +27,7 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.wm.shell.flicker"/>
+        <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
         <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
@@ -37,4 +38,4 @@
         <option name="collect-on-run-ended-only" value="true" />
         <option name="clean-up" value="true" />
     </metrics_collector>
-</configuration>
+</configuration>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
rename to libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
index 526fc50..afb1166 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
@@ -27,6 +27,7 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.wm.shell.flicker"/>
+        <option name="exclude-annotation" value="androidx.test.filters.RequiresDevice" />
         <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
@@ -37,4 +38,4 @@
         <option name="collect-on-run-ended-only" value="true" />
         <option name="clean-up" value="true" />
     </metrics_collector>
-</configuration>
+</configuration>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
index 308a36e..47a62ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
@@ -18,7 +18,7 @@
 
 import android.app.Instrumentation
 import android.support.test.launcherhelper.ILauncherStrategy
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 
 abstract class FlickerAppHelper(
     instr: Instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 4b04449..c3c576d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -18,7 +18,7 @@
 
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.expandPipWindow
@@ -41,7 +41,7 @@
  * Test Pip launch.
  * To run this test: `atest FlickerTests:PipToAppTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 692e2fa..937b00b 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -25,6 +25,7 @@
         "androidx.test.ext.junit",
         "mockito-target-extended-minus-junit4",
         "truth-prebuilt",
+        "testables",
     ],
     libs: [
         "android.test.mock",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 497b6b7..7b499d4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager.RunningTaskInfo;
@@ -32,6 +33,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.common.TransactionPool;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,6 +54,7 @@
     private ITaskOrganizerController mTaskOrganizerController;
 
     ShellTaskOrganizer mOrganizer;
+    private final TransactionPool mTransactionPool = mock(TransactionPool.class);
 
     private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
         final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -81,7 +85,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController);
+        mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mTransactionPool);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
index 7fabf82..a8a3a9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static org.junit.Assert.assertNotNull;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index e42cf52..1ce8b54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -112,11 +112,22 @@
     }
 
     @Test
-    public void testRegisterTransitionCallback() {
-        verify(mMockDisplayAreaOrganizer, atLeastOnce()).registerTransitionCallback(any());
+    public void testRegisterTransitionCallbackAfterInit() {
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTouchHandler);
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockGestureHandler);
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
     }
 
     @Test
+    public void testRegisterTransitionCallback() {
+        OneHandedTransitionCallback callback = new OneHandedTransitionCallback() {};
+        mOneHandedController.registerTransitionCallback(callback);
+
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(callback);
+    }
+
+
+    @Test
     public void testStopOneHanded_shouldRemoveTimer() {
         mOneHandedController.stopOneHanded();
 
@@ -139,7 +150,7 @@
         verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled);
     }
 
-    @Ignore("b/161980408, fix it after migration finished")
+    @Ignore("b/167943723, refactor it and fix it")
     @Test
     public void tesSettingsObserver_updateTapAppToExit() {
         Settings.Secure.putInt(mContext.getContentResolver(),
@@ -148,7 +159,7 @@
         verify(mOneHandedController).setTaskChangeToExit(true);
     }
 
-    @Ignore("b/161980408, fix it after migration finished")
+    @Ignore("b/167943723, refactor it and fix it")
     @Test
     public void tesSettingsObserver_updateEnabled() {
         Settings.Secure.putInt(mContext.getContentResolver(),
@@ -157,7 +168,7 @@
         verify(mOneHandedController).setOneHandedEnabled(true);
     }
 
-    @Ignore("b/161980408, fix it after migration finished")
+    @Ignore("b/167943723, refactor it and fix it")
     @Test
     public void tesSettingsObserver_updateTimeout() {
         Settings.Secure.putInt(mContext.getContentResolver(),
@@ -168,7 +179,7 @@
                 OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
     }
 
-    @Ignore("b/161980408, fix it after migration finished")
+    @Ignore("b/167943723, refactor it and fix it")
     @Test
     public void tesSettingsObserver_updateSwipeToNotification() {
         Settings.Secure.putInt(mContext.getContentResolver(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index a989cd1f..5ff94b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedEventsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedEventsTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
index 36c1174..492c34e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedEventsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static org.junit.Assert.assertEquals;
 
@@ -22,7 +22,6 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,7 +33,7 @@
 
 @RunWith(Parameterized.class)
 @SmallTest
-public class OneHandedEventsTest extends SysuiTestCase {
+public class OneHandedEventsTest extends OneHandedTestCase {
 
     private UiEventLoggerFake mUiEventLogger;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
new file mode 100644
index 0000000..fb417c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.wm.shell.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedGestureHandlerTest extends OneHandedTestCase {
+    OneHandedTutorialHandler mTutorialHandler;
+    OneHandedGestureHandler mGestureHandler;
+    @Mock
+    DisplayController mMockDisplayController;
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mTutorialHandler = new OneHandedTutorialHandler(mContext);
+        mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
+    }
+
+    @Test
+    public void testSetGestureEventListener() {
+        OneHandedGestureHandler.OneHandedGestureEventCallback callback = 
+            new OneHandedGestureHandler.OneHandedGestureEventCallback() {
+                @Override
+                public void onStart() {}
+
+                @Override
+                public void onStop() {}
+            };
+
+        mGestureHandler.setGestureEventListener(callback);
+        assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback);
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void testReceiveNewConfig_whenThreeButtonModeEnabled() {
+        mGestureHandler.onOneHandedEnabled(true);
+        mGestureHandler.onThreeButtonModeEnabled(true);
+
+        assertThat(mGestureHandler.mInputMonitor).isNotNull();
+        assertThat(mGestureHandler.mInputEventReceiver).isNotNull();
+    }
+
+    @Test
+    public void testOneHandedDisabled_shouldDisposeInputChannel() {
+        mGestureHandler.onOneHandedEnabled(false);
+
+        assertThat(mGestureHandler.mInputMonitor).isNull();
+        assertThat(mGestureHandler.mInputEventReceiver).isNull();
+    }
+
+    @Test
+    public void testChangeNavBarToNon3Button_shouldDisposeInputChannel() {
+        mGestureHandler.onOneHandedEnabled(true);
+        mGestureHandler.onThreeButtonModeEnabled(false);
+
+        assertThat(mGestureHandler.mInputMonitor).isNull();
+        assertThat(mGestureHandler.mInputEventReceiver).isNull();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
similarity index 83%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
index 990eb63..7c11138 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -59,24 +59,24 @@
     @Test
     public void testRegisterSecureKeyObserver() {
         final Uri result = OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver);
+                Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
 
         assertThat(result).isNotNull();
 
         OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver);
+                Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
     }
 
     @Test
     public void testUnregisterSecureKeyObserver() {
         OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver);
+                Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
         OneHandedSettingsUtil.unregisterSettingsKeyObserver(mContentResolver, mContentObserver);
 
         assertThat(mOnChanged).isFalse();
 
         Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, 0);
+                Settings.Secure.TAPS_APP_TO_EXIT, 0);
 
         assertThat(mOnChanged).isFalse();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
similarity index 73%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
index f111c48..c7ae2a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
@@ -14,17 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
 
 import static org.junit.Assume.assumeTrue;
 
+import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
 
-import com.android.systemui.SysuiTestCase;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.Before;
@@ -32,14 +36,26 @@
 /**
  * Base class that does One Handed specific setup.
  */
-public abstract class OneHandedTestCase extends SysuiTestCase {
+public abstract class OneHandedTestCase {
     static boolean sOrigEnabled;
     static boolean sOrigTapsAppToExitEnabled;
     static int sOrigTimeout;
     static boolean sOrigSwipeToNotification;
 
+    protected Context mContext;
+
     @Before
     public void setupSettings() {
+        final Context testContext =
+                InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final DisplayManager dm = testContext.getSystemService(DisplayManager.class);
+        mContext = testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY));
+
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+
         sOrigEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                 getContext().getContentResolver());
         sOrigTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
@@ -74,6 +90,15 @@
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
                 sOrigSwipeToNotification ? 1 : 0);
+
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    protected Context getContext() {
+        return mContext;
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTimeoutHandlerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
index 7d63131..e2b70c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTimeoutHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
new file mode 100644
index 0000000..c69e385
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.wm.shell.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedTouchHandlerTest extends OneHandedTestCase {
+    OneHandedTouchHandler mTouchHandler;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTouchHandler = new OneHandedTouchHandler();
+    }
+
+    @Test
+    public void testRegisterTouchEventListener() {
+        OneHandedTouchHandler.OneHandedTouchEventCallback callback = () -> {
+        };
+        mTouchHandler.registerTouchEventListener(callback);
+
+        assertThat(mTouchHandler.mTouchEventCallback).isEqualTo(callback);
+    }
+
+    @Test
+    public void testOneHandedDisabled_shouldDisposeInputChannel() {
+        mTouchHandler.onOneHandedEnabled(false);
+
+        assertThat(mTouchHandler.mInputMonitor).isNull();
+        assertThat(mTouchHandler.mInputEventReceiver).isNull();
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void testOneHandedEnabled_monitorInputChannel() {
+        mTouchHandler.onOneHandedEnabled(true);
+
+        assertThat(mTouchHandler.mInputMonitor).isNotNull();
+        assertThat(mTouchHandler.mInputEventReceiver).isNotNull();
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 8ea5524..4a133d39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static org.mockito.Mockito.verify;
 
@@ -29,13 +29,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
+    @Mock
     OneHandedTouchHandler mTouchHandler;
     OneHandedTutorialHandler mTutorialHandler;
     OneHandedGestureHandler mGestureHandler;
@@ -48,8 +48,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTouchHandler = new OneHandedTouchHandler();
-        mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext));
+        mTutorialHandler = new OneHandedTutorialHandler(mContext);
         mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
         mOneHandedController = new OneHandedController(
                 getContext(),
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index a23191f..523a072 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -15,8 +15,10 @@
  */
 package android.media;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
@@ -738,15 +740,16 @@
 
         /**
          * Create a Builder using a {@link MediaMetadata} instance to set
-         * initial values, but replace bitmaps with a scaled down copy if they
-         * are larger than maxBitmapSize.
+         * initial values, but replace bitmaps with a scaled down copy if their width (or height)
+         * is larger than maxBitmapSize.
          *
          * @param source The original metadata to copy.
          * @param maxBitmapSize The maximum height/width for bitmaps contained
          *            in the metadata.
          * @hide
          */
-        public Builder(MediaMetadata source, int maxBitmapSize) {
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        public Builder(@NonNull MediaMetadata source, @IntRange(from = 1) int maxBitmapSize) {
             this(source);
             for (String key : mBundle.keySet()) {
                 Object value = mBundle.get(key);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 36ae3ec..851c1ec 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,6 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentProvider;
@@ -2092,6 +2094,8 @@
         mOnInfoListener = null;
         mOnVideoSizeChangedListener = null;
         mOnTimedTextListener = null;
+        mOnImsRxNoticeListener = null;
+        mOnImsRxNoticeHandler = null;
         synchronized (mTimeProviderLock) {
             if (mTimeProvider != null) {
                 mTimeProvider.close();
@@ -3299,6 +3303,7 @@
     private static final int MEDIA_META_DATA = 202;
     private static final int MEDIA_DRM_INFO = 210;
     private static final int MEDIA_TIME_DISCONTINUITY = 211;
+    private static final int MEDIA_IMS_RX_NOTICE = 300;
     private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private TimeProvider mTimeProvider;
@@ -3608,6 +3613,35 @@
                 }
                 return;
 
+            case MEDIA_IMS_RX_NOTICE:
+                final OnImsRxNoticeListener imsRxNoticeListener;
+                final Handler imsRxNoticeHandler;
+                imsRxNoticeListener = mOnImsRxNoticeListener;
+                imsRxNoticeHandler = mOnImsRxNoticeHandler;
+                if (imsRxNoticeListener == null) {
+                    return;
+                }
+                if (msg.obj instanceof Parcel) {
+                    Parcel parcel = (Parcel) msg.obj;
+                    byte[] event;
+                    try {
+                        event = parcel.marshall();
+                    } finally {
+                        parcel.recycle();
+                    }
+                    if (imsRxNoticeHandler == null) {
+                        imsRxNoticeListener.onImsRxNotice(mMediaPlayer, event);
+                    } else {
+                        imsRxNoticeHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                imsRxNoticeListener.onImsRxNotice(mMediaPlayer, event);
+                            }
+                        });
+                    }
+                }
+                return;
+
             default:
                 Log.e(TAG, "Unknown message type " + msg.what);
                 return;
@@ -4048,6 +4082,146 @@
     }
 
     /**
+     * Interface definition of a callback to be invoked when
+     * IMS Rx connection has a notice.
+     *
+     * @see MediaPlayer.setOnImsRxNoticeListener
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OnImsRxNoticeListener
+    {
+        /**
+         * Called to indicate an IMS event noticed from native media frameworks.
+         * <p></p>
+         * Basic format. All TYPE and ARG are 4 bytes unsigned integer in native byte order.
+         * <pre>{@code
+         * 0                4               8                12
+         * +----------------+---------------+----------------+----------------+
+         * |      TYPE      |      ARG1     |      ARG2      |      ARG3      |
+         * +----------------+---------------+----------------+----------------+
+         * |      ARG4      |      ARG5     |      ...
+         * +----------------+---------------+-------------
+         * 16               20              24
+         *
+         *
+         * TYPE 100 - A notice of the first rtp packet received. No ARGs.
+         * 0
+         * +----------------+
+         * |      100       |
+         * +----------------+
+         *
+         *
+         * TYPE 101 - A notice of the first rtcp packet received. No ARGs.
+         * 0
+         * +----------------+
+         * |      101       |
+         * +----------------+
+         *
+         *
+         * TYPE 102 - A periodic report of a RTP statistics.
+         * TYPE 103 - An emergency report when serious packet loss has been detected
+         *            in between TYPE 102 events.
+         * 0                4               8                12
+         * +----------------+---------------+----------------+----------------+
+         * |   102 or 103   |   FB type=0   |    Bitrate     |   Top #.Seq    |
+         * +----------------+---------------+----------------+----------------+
+         * |   Base #.Seq   |Prev Expt #.Pkt|   Recv #.Pkt   |Prev Recv #.Pkt |
+         * +----------------+---------------+----------------+----------------+
+         * Feedback (FB) type
+         *      - always 0.
+         * Bitrate
+         *      - amount of data received in this period.
+         * Top number of sequence
+         *      - highest RTP sequence number received in this period.
+         *      - monotonically increasing value.
+         * Base number of sequence
+         *      - the first RTP sequence number of the media stream.
+         * Previous Expected number of Packets
+         *      - expected count of packets received in the previous report.
+         * Received number of packet
+         *      - actual count of packets received in this report.
+         * Previous Received number of packet
+         *      - actual count of packets received in the previous report.
+         *
+         *
+         * TYPE 205 - Transport layer Feedback message. (RFC-5104 Sec.4.2)
+         * 0                4               8                12
+         * +----------------+---------------+----------------+----------------+
+         * |      205       |      SSRC     | FB type(1 or 3)|     value      |
+         * +----------------+---------------+----------------+----------------+
+         * SSRC
+         *      - Remote side's SSRC value of the media sender (RFC-3550 Sec.5.1)
+         * Feedback (FB) type: determines the type of the event.
+         *      - if 1, we received a NACK request from the remote side.
+         *      - if 3, we received a TMMBR (Temporary Maximum Media Stream Bit Rate Request) from
+         *        the remote side.
+         * Value: the FCI (Feedback Control Information) depending on the value of FB type
+         *      - if FB type is 1, the Generic NACK as specified in RFC-4585 Sec.6.2.1
+         *      - if FB type is 3, the TMMBR as specified in RFC-5104 Sec.4.2.1.1
+         *
+         *
+         * TYPE 206 - Payload-specific Feedback message. (RFC-5104 Sec.4.3)
+         * 0                4               8
+         * +----------------+---------------+----------------+
+         * |      206       |      SSRC     | FB type(1 or 4)|
+         * +----------------+---------------+----------------+
+         * SSRC
+         *      - Remote side's SSRC value of the media sender (RFC-3550 Sec.5.1)
+         * Feedback (FB) type: determines the type of the event.
+         *      - if 1, we received a PLI request from the remote side.
+         *      - if 4, we received a FIR request from the remote side.
+         *
+         *
+         * TYPE 300 - CVO (RTP Extension) message.
+         * 0                4
+         * +----------------+---------------+
+         * |      101       |     value     |
+         * +----------------+---------------+
+         * value
+         *      - clockwise rotation degrees of a received video (6.2.3 of 3GPP R12 TS 26.114).
+         *      - can be 0 (degree 0), 1 (degree 90), 2 (degree 180) or 3 (degree 270).
+         *
+         *
+         * TYPE 400 - Socket failed during receive. No ARGs.
+         * 0
+         * +----------------+
+         * |      400       |
+         * +----------------+
+         * }</pre>
+         *
+         * @param mp the {@code MediaPlayer} associated with this callback.
+         * @param event an IMS media event serialized as byte[] array.
+         */
+        void onImsRxNotice(@NonNull MediaPlayer mp, @NonNull byte[] event);
+    }
+
+    /**
+     * Register a callback to be invoked when IMS Rx connection has a notice.
+     * The callback required if mediaplayer configured for RTPSource by
+     * MediaPlayer.setDataSource(String8 rtpParams) of mediaplayer.h
+     *
+     * @see MediaPlayer.OnImsRxNoticeListener
+     *
+     * @param listener the callback that will be run
+     * @param handler Specifies Handler object for the thread on which to execute
+     * the callback. If null, the handler on the main looper will be used.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission("android.permission.BIND_IMS_SERVICE")
+    public void setOnImsRxNoticeListener(
+        @Nullable OnImsRxNoticeListener listener, @Nullable Handler handler) {
+        mOnImsRxNoticeListener = listener;
+        mOnImsRxNoticeHandler = handler;
+    }
+
+    private OnImsRxNoticeListener mOnImsRxNoticeListener;
+    private Handler mOnImsRxNoticeHandler;
+
+    /**
      * Register a callback to be invoked when a selected track has timed metadata available.
      * <p>
      * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 549e793..624607b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -828,9 +828,10 @@
         }
 
         /**
-         * Gets the UID of this token.
+         * Gets the UID of the application that created the media session.
          * @hide
          */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
         public int getUid() {
             return mUid;
         }
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index bb40e24..26a0ad9 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -6532,6 +6532,7 @@
     method public android.graphics.Bitmap takeScreenshot();
     method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
     field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
+    field public static final int FLAG_DONT_USE_ACCESSIBILITY = 2; // 0x2
     field public static final int ROTATION_FREEZE_0 = 0; // 0x0
     field public static final int ROTATION_FREEZE_180 = 2; // 0x2
     field public static final int ROTATION_FREEZE_270 = 3; // 0x3
@@ -50983,6 +50984,7 @@
     method public final boolean isFunctionPressed();
     method public static final boolean isGamepadButton(int);
     method public final boolean isLongPress();
+    method public static final boolean isMediaSessionKey(int);
     method public final boolean isMetaPressed();
     method public static boolean isModifierKey(int);
     method public final boolean isNumLockOn();
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index aec4ff53..f7f42d0 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -41,6 +41,10 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+  }
+
 }
 
 package android.media.session {
@@ -53,6 +57,10 @@
     field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000
   }
 
+  public static final class MediaSession.Token implements android.os.Parcelable {
+    method public int getUid();
+  }
+
   public final class MediaSessionManager {
     method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
     method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index ff3999f..8fae240 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -205,6 +205,7 @@
     field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
     field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
+    field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
     field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
     field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -4277,6 +4278,14 @@
     method @NonNull public android.media.HwAudioSource.Builder setAudioDeviceInfo(@NonNull android.media.AudioDeviceInfo);
   }
 
+  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
+    method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnImsRxNoticeListener(@Nullable android.media.MediaPlayer.OnImsRxNoticeListener, @Nullable android.os.Handler);
+  }
+
+  public static interface MediaPlayer.OnImsRxNoticeListener {
+    method public void onImsRxNotice(@NonNull android.media.MediaPlayer, @NonNull byte[]);
+  }
+
   public final class MediaRecorder.AudioSource {
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
@@ -6812,6 +6821,8 @@
     field public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 8; // 0x8
     field public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 128; // 0x80
     field public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 16384; // 0x4000
+    field public static final int BSS_CAPABILITY_DMG_ESS = 3; // 0x3
+    field public static final int BSS_CAPABILITY_DMG_IBSS = 1; // 0x1
     field public static final int BSS_CAPABILITY_DSSS_OFDM = 8192; // 0x2000
     field public static final int BSS_CAPABILITY_ESS = 1; // 0x1
     field public static final int BSS_CAPABILITY_IBSS = 2; // 0x2
diff --git a/non-updatable-api/system-lint-baseline.txt b/non-updatable-api/system-lint-baseline.txt
index 395ddc1..4db55e7 100644
--- a/non-updatable-api/system-lint-baseline.txt
+++ b/non-updatable-api/system-lint-baseline.txt
@@ -6,7 +6,8 @@
 BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
     Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
 
-
+ExecutorRegistration: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler):
+    
 GenericException: android.app.prediction.AppPredictor#finalize():
     
 GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -181,6 +182,8 @@
     
 SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
     
+SamShouldBeLast: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler):
+    
 SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
     
 SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 1fe2760..949a0fa 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -53,6 +53,14 @@
     <integer name="config_rightSystemBarZOrder">0</integer>
     <integer name="config_bottomSystemBarZOrder">10</integer>
 
+    <!-- If set to true, the corresponding system bar will be hidden when Keyboard (IME) appears.
+         NOTE: hideBottomSystemBarKeyboard must not be overlaid directly here. To change its value,
+         overlay config_automotiveHideNavBarForKeyboard in framework/base/core/res/res. -->
+    <bool name="config_hideTopSystemBarForKeyboard">false</bool>
+    <bool name="config_hideBottomSystemBarForKeyboard">@*android:bool/config_automotiveHideNavBarForKeyboard</bool>
+    <bool name="config_hideLeftSystemBarForKeyboard">false</bool>
+    <bool name="config_hideRightSystemBarForKeyboard">false</bool>
+
     <!-- Disable normal notification rendering; we handle that ourselves -->
     <bool name="config_renderNotifications">false</bool>
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index dadbc22..3af7507 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -30,7 +30,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
 import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.car.navigationbar.CarNavigationBarController;
@@ -40,6 +39,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.Factory;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -66,7 +66,7 @@
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     private final ViewMediatorCallback mViewMediatorCallback;
     private final CarNavigationBarController mCarNavigationBarController;
-    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+    private final Factory mKeyguardBouncerFactory;
     // Needed to instantiate mBouncer.
     private final KeyguardBouncer.BouncerExpansionCallback
             mExpansionCallback = new KeyguardBouncer.BouncerExpansionCallback() {
@@ -107,7 +107,7 @@
             Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
             ViewMediatorCallback viewMediatorCallback,
             CarNavigationBarController carNavigationBarController,
-            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
+            KeyguardBouncer.Factory keyguardBouncerFactory) {
 
         super(R.id.keyguard_stub, overlayViewGlobalStateController);
 
@@ -118,7 +118,7 @@
         mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
         mViewMediatorCallback = viewMediatorCallback;
         mCarNavigationBarController = carNavigationBarController;
-        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
+        mKeyguardBouncerFactory = keyguardBouncerFactory;
 
         registerUserSwitchedListener();
     }
@@ -130,9 +130,8 @@
 
     @Override
     public void onFinishInflate() {
-        mBouncer = mKeyguardBouncerComponentFactory
-                .build(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback)
-                .createKeyguardBouncer();
+        mBouncer = mKeyguardBouncerFactory
+                .create(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback);
         mBiometricUnlockControllerLazy.get().setKeyguardViewController(this);
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java
index aa6da89..2dc4756 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.car.navigationbar;
 
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -97,27 +97,27 @@
      * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
      * for consideration if it has the same displayId as the CarNavigationButton.
      *
-     * @param stackInfoList of the currently running application
+     * @param taskInfoList of the currently running application
      * @param validDisplay index of the valid display
      */
 
-    protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList, int validDisplay) {
-        ActivityManager.StackInfo validStackInfo = null;
-        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
+    protected void taskChanged(List<RootTaskInfo> taskInfoList, int validDisplay) {
+        RootTaskInfo validTaskInfo = null;
+        for (RootTaskInfo taskInfo : taskInfoList) {
             // Find the first stack info with a topActivity in the primary display.
             // TODO: We assume that CarFacetButton will launch an app only in the primary display.
             // We need to extend the functionality to handle the multiple display properly.
-            if (stackInfo.topActivity != null && stackInfo.displayId == validDisplay) {
-                validStackInfo = stackInfo;
+            if (taskInfo.topActivity != null && taskInfo.displayId == validDisplay) {
+                validTaskInfo = taskInfo;
                 break;
             }
         }
 
-        if (validStackInfo == null) {
+        if (validTaskInfo == null) {
             // No stack was found that was on the same display as the buttons thus return
             return;
         }
-        int displayId = validStackInfo.displayId;
+        int displayId = validTaskInfo.displayId;
 
         mSelectedButtons.forEach(carNavigationButton -> {
             if (carNavigationButton.getDisplayId() == displayId) {
@@ -126,7 +126,7 @@
         });
         mSelectedButtons.clear();
 
-        HashSet<CarNavigationButton> selectedButtons = findSelectedButtons(validStackInfo);
+        HashSet<CarNavigationButton> selectedButtons = findSelectedButtons(validTaskInfo);
 
         if (selectedButtons != null) {
             selectedButtons.forEach(carNavigationButton -> {
@@ -141,10 +141,10 @@
     /**
      * Defaults to Display.DEFAULT_DISPLAY when no parameter is provided for the validDisplay.
      *
-     * @param stackInfoList
+     * @param taskInfoList
      */
-    protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
-        taskChanged(stackInfoList, Display.DEFAULT_DISPLAY);
+    protected void taskChanged(List<RootTaskInfo> taskInfoList) {
+        taskChanged(taskInfoList, Display.DEFAULT_DISPLAY);
     }
 
     /**
@@ -171,12 +171,11 @@
         mRegisteredViews.add(carNavigationButton);
     }
 
-    private HashSet<CarNavigationButton> findSelectedButtons(
-            ActivityManager.StackInfo validStackInfo) {
-        String packageName = validStackInfo.topActivity.getPackageName();
+    private HashSet<CarNavigationButton> findSelectedButtons(RootTaskInfo validTaskInfo) {
+        String packageName = validTaskInfo.topActivity.getPackageName();
 
         HashSet<CarNavigationButton> selectedButtons =
-                findButtonsByComponentName(validStackInfo.topActivity);
+                findButtonsByComponentName(validTaskInfo.topActivity);
         if (selectedButtons == null) {
             selectedButtons = mButtonsByPackage.get(packageName);
         }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java
index d6216ba..f74bd4f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java
@@ -43,9 +43,9 @@
     public void onTaskStackChanged() {
         try {
             mButtonSelectionStateController.taskChanged(
-                    ActivityTaskManager.getService().getAllStackInfos());
+                    ActivityTaskManager.getService().getAllRootTaskInfos());
         } catch (Exception e) {
-            Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+            Log.e(TAG, "Getting RootTaskInfo from activity task manager failed", e);
         }
     }
 
@@ -53,9 +53,9 @@
     public void onTaskDisplayChanged(int taskId, int newDisplayId) {
         try {
             mButtonSelectionStateController.taskChanged(
-                    ActivityTaskManager.getService().getAllStackInfos());
+                    ActivityTaskManager.getService().getAllRootTaskInfos());
         } catch (Exception e) {
-            Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+            Log.e(TAG, "Getting RootTaskInfo from activity task manager failed", e);
         }
 
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index bf8cda3..c7db3f6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -91,7 +91,11 @@
     private ActivityManagerWrapper mActivityManagerWrapper;
 
     // If the nav bar should be hidden when the soft keyboard is visible.
-    private boolean mHideNavBarForKeyboard;
+    private boolean mHideTopBarForKeyboard;
+    private boolean mHideLeftBarForKeyboard;
+    private boolean mHideRightBarForKeyboard;
+    private boolean mHideBottomBarForKeyboard;
+
     private boolean mBottomNavBarVisible;
 
     // Nav bar views.
@@ -160,8 +164,13 @@
     @Override
     public void start() {
         // Set initial state.
-        mHideNavBarForKeyboard = mResources.getBoolean(
-                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+        mHideTopBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP);
+        mHideBottomBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(
+                SystemBarConfigs.BOTTOM);
+        mHideLeftBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.LEFT);
+        mHideRightBarForKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(
+                SystemBarConfigs.RIGHT);
+
         mBottomNavBarVisible = false;
 
         // Connect into the status bar manager service
@@ -411,17 +420,30 @@
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
-        if (!mHideNavBarForKeyboard) {
-            return;
-        }
-
         if (mContext.getDisplayId() != displayId) {
             return;
         }
 
         boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
-        mCarNavigationBarController.setBottomWindowVisibility(
-                isKeyboardVisible ? View.GONE : View.VISIBLE);
+
+        if (mHideTopBarForKeyboard) {
+            mCarNavigationBarController.setTopWindowVisibility(
+                    isKeyboardVisible ? View.GONE : View.VISIBLE);
+        }
+
+        if (mHideBottomBarForKeyboard) {
+            mCarNavigationBarController.setBottomWindowVisibility(
+                    isKeyboardVisible ? View.GONE : View.VISIBLE);
+        }
+
+        if (mHideLeftBarForKeyboard) {
+            mCarNavigationBarController.setLeftWindowVisibility(
+                    isKeyboardVisible ? View.GONE : View.VISIBLE);
+        }
+        if (mHideRightBarForKeyboard) {
+            mCarNavigationBarController.setRightWindowVisibility(
+                    isKeyboardVisible ? View.GONE : View.VISIBLE);
+        }
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
index 529083f4b..4fb5220 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
@@ -83,9 +83,7 @@
      * Hides all system bars.
      */
     public void hideBars() {
-        if (mTopView != null) {
-            mTopView.setVisibility(View.GONE);
-        }
+        setTopWindowVisibility(View.GONE);
         setBottomWindowVisibility(View.GONE);
         setLeftWindowVisibility(View.GONE);
         setRightWindowVisibility(View.GONE);
@@ -95,9 +93,7 @@
      * Shows all system bars.
      */
     public void showBars() {
-        if (mTopView != null) {
-            mTopView.setVisibility(View.VISIBLE);
-        }
+        setTopWindowVisibility(View.VISIBLE);
         setBottomWindowVisibility(View.VISIBLE);
         setLeftWindowVisibility(View.VISIBLE);
         setRightWindowVisibility(View.VISIBLE);
@@ -140,6 +136,11 @@
         return mShowRight ? mNavigationBarViewFactory.getRightWindow() : null;
     }
 
+    /** Toggles the top nav bar visibility. */
+    public boolean setTopWindowVisibility(@View.Visibility int visibility) {
+        return setWindowVisibility(getTopWindow(), visibility);
+    }
+
     /** Toggles the bottom nav bar visibility. */
     public boolean setBottomWindowVisibility(@View.Visibility int visibility) {
         return setWindowVisibility(getBottomWindow(), visibility);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index 1418ace..078196e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -100,6 +100,7 @@
         checkEnabledBarsHaveUniqueBarTypes();
         checkAllOverlappingBarsHaveDifferentZOrders();
         checkSystemBarEnabledForNotificationPanel();
+        checkHideBottomBarForKeyboardConfigSync();
         setInsetPaddingsForOverlappingCorners();
         sortSystemBarSidesByZOrder();
     }
@@ -124,6 +125,11 @@
         }
     }
 
+    protected boolean getHideForKeyboardBySide(@SystemBarSide int side) {
+        return mSystemBarConfigMap.get(side) != null
+                && mSystemBarConfigMap.get(side).getHideForKeyboard();
+    }
+
     protected void insetSystemBar(@SystemBarSide int side, CarNavigationBarView view) {
         if (mSystemBarConfigMap.get(side) == null) return;
 
@@ -213,6 +219,8 @@
                                     com.android.internal.R.dimen.status_bar_height))
                             .setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
                             .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
+                            .setHideForKeyboard(mResources.getBoolean(
+                                    R.bool.config_hideTopSystemBarForKeyboard))
                             .build();
             mSystemBarConfigMap.put(TOP, topBarConfig);
         }
@@ -226,6 +234,8 @@
                             .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
                             .setZOrder(
                                     mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
+                            .setHideForKeyboard(mResources.getBoolean(
+                                    R.bool.config_hideBottomSystemBarForKeyboard))
                             .build();
             mSystemBarConfigMap.put(BOTTOM, bottomBarConfig);
         }
@@ -238,6 +248,8 @@
                                     R.dimen.car_left_navigation_bar_width))
                             .setBarType(mResources.getInteger(R.integer.config_leftSystemBarType))
                             .setZOrder(mResources.getInteger(R.integer.config_leftSystemBarZOrder))
+                            .setHideForKeyboard(mResources.getBoolean(
+                                    R.bool.config_hideLeftSystemBarForKeyboard))
                             .build();
             mSystemBarConfigMap.put(LEFT, leftBarConfig);
         }
@@ -250,6 +262,8 @@
                                     R.dimen.car_right_navigation_bar_width))
                             .setBarType(mResources.getInteger(R.integer.config_rightSystemBarType))
                             .setZOrder(mResources.getInteger(R.integer.config_rightSystemBarZOrder))
+                            .setHideForKeyboard(mResources.getBoolean(
+                                    R.bool.config_hideRightSystemBarForKeyboard))
                             .build();
             mSystemBarConfigMap.put(RIGHT, rightBarConfig);
         }
@@ -291,19 +305,37 @@
             e.printStackTrace();
         }
 
-        if (!mTopNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
-                TopNotificationPanelViewMediator.class)) {
+        if (!mTopNavBarEnabled && TopNotificationPanelViewMediator.class.isAssignableFrom(
+                notificationPanelMediatorUsed)) {
             throw new RuntimeException(
                     "Top System Bar must be enabled to use " + notificationPanelMediatorName);
         }
 
-        if (!mBottomNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
-                BottomNotificationPanelViewMediator.class)) {
+        if (!mBottomNavBarEnabled && BottomNotificationPanelViewMediator.class.isAssignableFrom(
+                notificationPanelMediatorUsed)) {
             throw new RuntimeException("Bottom System Bar must be enabled to use "
                     + notificationPanelMediatorName);
         }
     }
 
+    private void checkHideBottomBarForKeyboardConfigSync() throws RuntimeException {
+        if (mBottomNavBarEnabled) {
+            boolean actual = mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard);
+            boolean expected = mResources.getBoolean(
+                    com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+
+            if (actual != expected) {
+                throw new RuntimeException("config_hideBottomSystemBarForKeyboard must not be "
+                        + "overlaid directly and should always refer to"
+                        + "config_automotiveHideNavBarForKeyboard. However, their values "
+                        + "currently do not sync. Set config_hideBottomSystemBarForKeyguard to "
+                        + "@*android:bool/config_automotiveHideNavBarForKeyboard. To change its "
+                        + "value, overlay config_automotiveHideNavBarForKeyboard in "
+                        + "framework/base/core/res/res.");
+            }
+        }
+    }
+
     private void setInsetPaddingsForOverlappingCorners() {
         Map<@SystemBarSide Integer, Boolean> systemBarVisibilityOnInit =
                 getSystemBarsVisibilityOnInit();
@@ -410,14 +442,17 @@
         private final int mBarType;
         private final int mGirth;
         private final int mZOrder;
+        private final boolean mHideForKeyboard;
 
         private int[] mPaddings = new int[]{0, 0, 0, 0};
 
-        private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder) {
+        private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder,
+                boolean hideForKeyboard) {
             mSide = side;
             mBarType = barType;
             mGirth = girth;
             mZOrder = zOrder;
+            mHideForKeyboard = hideForKeyboard;
         }
 
         private int getSide() {
@@ -436,6 +471,10 @@
             return mZOrder;
         }
 
+        private boolean getHideForKeyboard() {
+            return mHideForKeyboard;
+        }
+
         private int[] getPaddings() {
             return mPaddings;
         }
@@ -473,6 +512,7 @@
         private int mBarType;
         private int mGirth;
         private int mZOrder;
+        private boolean mHideForKeyboard;
 
         private SystemBarConfigBuilder setSide(@SystemBarSide int side) {
             mSide = side;
@@ -494,8 +534,13 @@
             return this;
         }
 
+        private SystemBarConfigBuilder setHideForKeyboard(boolean hide) {
+            mHideForKeyboard = hide;
+            return this;
+        }
+
         private SystemBarConfig build() {
-            return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder);
+            return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard);
         }
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
index eb32edb..f96ee0f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
@@ -17,7 +17,7 @@
 package com.android.systemui.car.sideloaded;
 
 import android.annotation.NonNull;
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstallSourceInfo;
@@ -78,10 +78,10 @@
         return false;
     }
 
-    boolean isSafe(@NonNull ActivityManager.StackInfo stackInfo) {
-        ComponentName componentName = stackInfo.topActivity;
+    boolean isSafe(@NonNull RootTaskInfo taskInfo) {
+        ComponentName componentName = taskInfo.topActivity;
         if (componentName == null) {
-            Log.w(TAG, "Stack info does not have top activity: " + stackInfo.stackId);
+            Log.w(TAG, "Task info does not have top activity: " + taskInfo.taskId);
             return false;
         }
         return isSafe(componentName.getPackageName());
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppListener.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppListener.java
index c8c1a40..db7718b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppListener.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppListener.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.car.sideloaded;
 
-import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
@@ -56,15 +55,15 @@
     public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
         super.onTaskCreated(taskId, componentName);
 
-        List<StackInfo> stackInfoList = mActivityTaskManager.getAllStackInfos();
-        ActivityManager.StackInfo stackInfo = getStackInfo(stackInfoList, taskId);
-        if (stackInfo == null) {
+        List<RootTaskInfo> taskInfoList = mActivityTaskManager.getAllRootTaskInfos();
+        RootTaskInfo taskInfo = getStackInfo(taskInfoList, taskId);
+        if (taskInfo == null) {
             Log.e(TAG, "Stack info was not available for taskId: " + taskId);
             return;
         }
 
-        if (!mSideLoadedAppDetector.isSafe(stackInfo)) {
-            Display display = mDisplayManager.getDisplay(stackInfo.displayId);
+        if (!mSideLoadedAppDetector.isSafe(taskInfo)) {
+            Display display = mDisplayManager.getDisplay(taskInfo.displayId);
             mSideLoadedAppStateController.onUnsafeTaskCreatedOnDisplay(display);
         }
     }
@@ -75,18 +74,18 @@
 
         Display[] displays = mDisplayManager.getDisplays();
         for (Display display : displays) {
-            // Note that the stackInfoList is ordered by recency.
-            List<StackInfo> stackInfoList =
-                    mActivityTaskManager.getAllStackInfosOnDisplay(display.getDisplayId());
+            // Note that the taskInfoList is ordered by recency.
+            List<RootTaskInfo> taskInfoList =
+                    mActivityTaskManager.getAllRootTaskInfosOnDisplay(display.getDisplayId());
 
-            if (stackInfoList == null) {
+            if (taskInfoList == null) {
                 continue;
             }
-            StackInfo stackInfo = getTopVisibleStackInfo(stackInfoList);
-            if (stackInfo == null) {
+            RootTaskInfo taskInfo = getTopVisibleStackInfo(taskInfoList);
+            if (taskInfo == null) {
                 continue;
             }
-            if (mSideLoadedAppDetector.isSafe(stackInfo)) {
+            if (mSideLoadedAppDetector.isSafe(taskInfo)) {
                 mSideLoadedAppStateController.onSafeTaskDisplayedOnDisplay(display);
             } else {
                 mSideLoadedAppStateController.onUnsafeTaskDisplayedOnDisplay(display);
@@ -97,18 +96,17 @@
     /**
      * Returns stack info for a given taskId.
      */
-    private ActivityManager.StackInfo getStackInfo(
-            List<ActivityManager.StackInfo> stackInfoList, int taskId) {
-        if (stackInfoList == null) {
+    private RootTaskInfo getStackInfo(List<RootTaskInfo> taskInfoList, int taskId) {
+        if (taskInfoList == null) {
             return null;
         }
-        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
-            if (stackInfo.taskIds == null) {
+        for (RootTaskInfo taskInfo : taskInfoList) {
+            if (taskInfo.childTaskIds == null) {
                 continue;
             }
-            for (int stackTaskId : stackInfo.taskIds) {
-                if (taskId == stackTaskId) {
-                    return stackInfo;
+            for (int taskTaskId : taskInfo.childTaskIds) {
+                if (taskId == taskTaskId) {
+                    return taskInfo;
                 }
             }
         }
@@ -118,11 +116,10 @@
     /**
      * Returns the first visible stackInfo.
      */
-    private ActivityManager.StackInfo getTopVisibleStackInfo(
-            List<ActivityManager.StackInfo> stackInfoList) {
-        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
-            if (stackInfo.visible) {
-                return stackInfo;
+    private RootTaskInfo getTopVisibleStackInfo(List<RootTaskInfo> taskInfoList) {
+        for (RootTaskInfo taskInfo : taskInfoList) {
+            if (taskInfo.visible) {
+                return taskInfo;
             }
         }
         return null;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java
index 5ef8aa1..1b1a118 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java
@@ -59,6 +59,8 @@
         }
     };
 
+    private boolean mUserLifecycleListenerRegistered = false;
+
     private final CarUserManager.UserLifecycleListener mUserLifecycleListener =
             new CarUserManager.UserLifecycleListener() {
                 @Override
@@ -100,9 +102,13 @@
      * Clean up the controller and unregister receiver.
      */
     public void removeAll() {
-        mBroadcastDispatcher.unregisterReceiver(mUserUpdateReceiver);
-        if (mCarUserManager != null) {
-            mCarUserManager.removeListener(mUserLifecycleListener);
+        mUserNameView = null;
+        if (mUserLifecycleListenerRegistered) {
+            mBroadcastDispatcher.unregisterReceiver(mUserUpdateReceiver);
+            if (mCarUserManager != null) {
+                mCarUserManager.removeListener(mUserLifecycleListener);
+            }
+            mUserLifecycleListenerRegistered = false;
         }
     }
 
@@ -112,6 +118,7 @@
             mCarUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE);
             if (mCarUserManager != null) {
                 mCarUserManager.addListener(Runnable::run, mUserLifecycleListener);
+                mUserLifecycleListenerRegistered = true;
             } else {
                 Log.e(TAG, "CarUserManager could not be obtained.");
             }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 63d4004..062ab41 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -36,7 +36,6 @@
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarServiceProvider;
@@ -67,9 +66,7 @@
     @Mock
     private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener;
     @Mock
-    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
-    @Mock
-    private KeyguardBouncerComponent mKeyguardBouncerComponent;
+    private KeyguardBouncer.Factory mKeyguardBouncerFactory;
     @Mock
     private KeyguardBouncer mBouncer;
 
@@ -77,11 +74,10 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        when(mKeyguardBouncerComponentFactory.build(
+        when(mKeyguardBouncerFactory.create(
                 any(ViewGroup.class),
                 any(KeyguardBouncer.BouncerExpansionCallback.class)))
-                .thenReturn(mKeyguardBouncerComponent);
-        when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer);
+                .thenReturn(mBouncer);
 
         mCarKeyguardViewController = new CarKeyguardViewController(
                 Handler.getMain(),
@@ -92,7 +88,7 @@
                 () -> mock(BiometricUnlockController.class),
                 mock(ViewMediatorCallback.class),
                 mock(CarNavigationBarController.class),
-                mKeyguardBouncerComponentFactory
+                mKeyguardBouncerFactory
         );
         mCarKeyguardViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
                 R.layout.sysui_overlay_window, /* root= */ null));
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
index f623c26..bd017cd 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.ComponentName;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -71,7 +71,7 @@
     public void onTaskChanged_buttonDetectableByComponentName_selectsAssociatedButton() {
         CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_component_name);
         mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
-        List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+        List<RootTaskInfo> testStack = createTestStack(mComponentName);
         testButton.setSelected(false);
         mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
 
@@ -82,7 +82,7 @@
     public void onTaskChanged_buttonDetectableByCategory_selectsAssociatedButton() {
         CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_category);
         mComponentName = new ComponentName(TEST_CATEGORY, TEST_CATEGORY_CLASS);
-        List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+        List<RootTaskInfo> testStack = createTestStack(mComponentName);
         testButton.setSelected(false);
         mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
 
@@ -93,7 +93,7 @@
     public void onTaskChanged_buttonDetectableByPackage_selectsAssociatedButton() {
         CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_package);
         mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
-        List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+        List<RootTaskInfo> testStack = createTestStack(mComponentName);
         testButton.setSelected(false);
         mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
 
@@ -104,12 +104,12 @@
     public void onTaskChanged_deselectsPreviouslySelectedButton() {
         CarNavigationButton oldButton = mTestView.findViewById(R.id.detectable_by_component_name);
         mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
-        List<ActivityManager.StackInfo> oldStack = createTestStack(mComponentName);
+        List<RootTaskInfo> oldStack = createTestStack(mComponentName);
         oldButton.setSelected(false);
         mButtonSelectionStateController.taskChanged(oldStack, /* validDisplay= */ -1);
 
         mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
-        List<ActivityManager.StackInfo> newStack = createTestStack(mComponentName);
+        List<RootTaskInfo> newStack = createTestStack(mComponentName);
         mButtonSelectionStateController.taskChanged(newStack, /* validDisplay= */ -1);
 
         assertButtonUnselected(oldButton);
@@ -125,12 +125,12 @@
         assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
     }
 
-    private List<ActivityManager.StackInfo> createTestStack(ComponentName componentName) {
-        ActivityManager.StackInfo validStackInfo = new ActivityManager.StackInfo();
+    private List<RootTaskInfo> createTestStack(ComponentName componentName) {
+        RootTaskInfo validStackInfo = new RootTaskInfo();
         validStackInfo.displayId = -1; // No display is assigned to this test view
         validStackInfo.topActivity = componentName;
 
-        List<ActivityManager.StackInfo> testStack = new ArrayList<>();
+        List<RootTaskInfo> testStack = new ArrayList<>();
         testStack.add(validStackInfo);
 
         return testStack;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
index 072358b..0c62f8b 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.car.navigationbar;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -32,7 +33,14 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarDeviceProvisionedController;
 import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.car.notification.NotificationPanelViewController;
+import com.android.systemui.car.notification.NotificationPanelViewMediator;
+import com.android.systemui.car.notification.PowerManagerHelper;
+import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,8 +62,6 @@
     private SystemBarConfigs mSystemBarConfigs;
     @Mock
     private Resources mResources;
-    @Mock
-    private CarNavigationBarView mCarNavigationBarView;
 
     @Before
     public void setUp() {
@@ -97,6 +103,43 @@
         mSystemBarConfigs = new SystemBarConfigs(mResources);
     }
 
+    @Test(expected = RuntimeException.class)
+    public void onInit_hideBottomSystemBarForKeyboardValueDoNotSync_throwsRuntimeException() {
+        when(mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard)).thenReturn(false);
+        when(mResources.getBoolean(
+                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard)).thenReturn(
+                true);
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
+    @Test
+    public void onInit_topNotifPanelViewMediatorUsed_topBarEnabled_doesNotThrowException() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(true);
+        when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+                TestTopNotificationPanelViewMediator.class.getName());
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void onInit_topNotifPanelViewMediatorUsed_topBarNotEnabled_throwsRuntimeException() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+        when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+                TestTopNotificationPanelViewMediator.class.getName());
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
+    @Test
+    public void onInit_notificationPanelViewMediatorUsed_topBarNotEnabled_doesNotThrowException() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+        when(mResources.getString(R.string.config_notificationPanelViewMediator)).thenReturn(
+                NotificationPanelViewMediator.class.getName());
+
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+    }
+
     @Test
     public void getTopSystemBarLayoutParams_topBarEnabled_returnsTopSystemBarLayoutParams() {
         mSystemBarConfigs = new SystemBarConfigs(mResources);
@@ -117,6 +160,26 @@
     }
 
     @Test
+    public void getTopSystemBarHideForKeyboard_hideBarForKeyboard_returnsTrue() {
+        when(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).thenReturn(true);
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+
+        boolean hideKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP);
+
+        assertTrue(hideKeyboard);
+    }
+
+    @Test
+    public void getTopSystemBarHideForKeyboard_topBarNotEnabled_returnsFalse() {
+        when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(false);
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+
+        boolean hideKeyboard = mSystemBarConfigs.getHideForKeyboardBySide(SystemBarConfigs.TOP);
+
+        assertFalse(hideKeyboard);
+    }
+
+    @Test
     public void topSystemBarHasHigherZOrderThanHuns_topSystemBarIsNavigationBarPanelType() {
         when(mResources.getInteger(R.integer.config_topSystemBarZOrder)).thenReturn(
                 SystemBarConfigs.getHunZOrder() + 1);
@@ -198,5 +261,30 @@
         when(mResources.getInteger(R.integer.config_bottomSystemBarZOrder)).thenReturn(10);
         when(mResources.getInteger(R.integer.config_leftSystemBarZOrder)).thenReturn(2);
         when(mResources.getInteger(R.integer.config_rightSystemBarZOrder)).thenReturn(3);
+
+        when(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).thenReturn(false);
+        when(mResources.getBoolean(
+                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard)).thenReturn(
+                false);
+        when(mResources.getBoolean(R.bool.config_hideLeftSystemBarForKeyboard)).thenReturn(
+                false);
+        when(mResources.getBoolean(R.bool.config_hideRightSystemBarForKeyboard)).thenReturn(
+                false);
+    }
+
+    // Intentionally using a subclass of TopNotificationPanelViewMediator for testing purposes to
+    // ensure that OEM's will be able to implement and use their own NotificationPanelViewMediator.
+    private class TestTopNotificationPanelViewMediator extends
+            TopNotificationPanelViewMediator {
+        TestTopNotificationPanelViewMediator(
+                CarNavigationBarController carNavigationBarController,
+                NotificationPanelViewController notificationPanelViewController,
+                PowerManagerHelper powerManagerHelper,
+                BroadcastDispatcher broadcastDispatcher,
+                CarDeviceProvisionedController carDeviceProvisionedController,
+                ConfigurationController configurationController) {
+            super(carNavigationBarController, notificationPanelViewController, powerManagerHelper,
+                    broadcastDispatcher, carDeviceProvisionedController, configurationController);
+        }
     }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
index 421e210..bf9ac30 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppDetectorTest.java
@@ -23,7 +23,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstallSourceInfo;
@@ -79,8 +79,8 @@
 
     @Test
     public void isSafe_systemApp_returnsTrue() throws Exception {
-        ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
-        stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+        RootTaskInfo taskInfo = new RootTaskInfo();
+        taskInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.packageName = APP_PACKAGE_NAME;
@@ -89,13 +89,13 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
                 .thenReturn(applicationInfo);
 
-        assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isTrue();
+        assertThat(mSideLoadedAppDetector.isSafe(taskInfo)).isTrue();
     }
 
     @Test
     public void isSafe_updatedSystemApp_returnsTrue() throws Exception {
-        ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
-        stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+        RootTaskInfo taskInfo = new RootTaskInfo();
+        taskInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.packageName = APP_PACKAGE_NAME;
@@ -104,7 +104,7 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(APP_PACKAGE_NAME), anyInt(), any()))
                 .thenReturn(applicationInfo);
 
-        assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isTrue();
+        assertThat(mSideLoadedAppDetector.isSafe(taskInfo)).isTrue();
     }
 
     @Test
@@ -113,8 +113,8 @@
                 /* initiatingPackageSigningInfo= */null,
                 /* originatingPackageName= */ null,
                 /* installingPackageName= */ null);
-        ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
-        stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+        RootTaskInfo taskInfo = new RootTaskInfo();
+        taskInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.packageName = APP_PACKAGE_NAME;
@@ -123,7 +123,7 @@
                 .thenReturn(applicationInfo);
         when(mPackageManager.getInstallSourceInfo(APP_PACKAGE_NAME)).thenReturn(sourceInfo);
 
-        assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isTrue();
+        assertThat(mSideLoadedAppDetector.isSafe(taskInfo)).isTrue();
     }
 
     @Test
@@ -132,8 +132,8 @@
                 /* initiatingPackageSigningInfo= */null,
                 /* originatingPackageName= */ null,
                 /* installingPackageName= */ null);
-        ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
-        stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+        RootTaskInfo taskInfo = new RootTaskInfo();
+        taskInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.packageName = APP_PACKAGE_NAME;
@@ -142,7 +142,7 @@
                 .thenReturn(applicationInfo);
         when(mPackageManager.getInstallSourceInfo(APP_PACKAGE_NAME)).thenReturn(sourceInfo);
 
-        assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isFalse();
+        assertThat(mSideLoadedAppDetector.isSafe(taskInfo)).isFalse();
     }
 
     @Test
@@ -151,8 +151,8 @@
                 /* initiatingPackageSigningInfo= */null,
                 /* originatingPackageName= */ null,
                 /* installingPackageName= */ null);
-        ActivityManager.StackInfo stackInfo = new ActivityManager.StackInfo();
-        stackInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
+        RootTaskInfo taskInfo = new RootTaskInfo();
+        taskInfo.topActivity = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.packageName = APP_PACKAGE_NAME;
@@ -161,6 +161,6 @@
                 .thenReturn(applicationInfo);
         when(mPackageManager.getInstallSourceInfo(APP_PACKAGE_NAME)).thenReturn(sourceInfo);
 
-        assertThat(mSideLoadedAppDetector.isSafe(stackInfo)).isFalse();
+        assertThat(mSideLoadedAppDetector.isSafe(taskInfo)).isFalse();
     }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
index 67f222b..0b5f68f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/SideLoadedAppListenerTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.hardware.display.DisplayManager;
@@ -81,22 +81,22 @@
         int displayId = 123;
         ComponentName componentName = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
-        StackInfo stackInfo1 = createTask(1, /* isVisible= */ true);
-        stackInfo1.taskIds = new int[] { 11, 22, 33 };
+        RootTaskInfo taskInfo1 = createTask(1, /* isVisible= */ true);
+        taskInfo1.childTaskIds = new int[] { 11, 22, 33 };
 
-        StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
-        stackInfo2.taskIds = new int[] { 111, 222, 333, taskId };
-        stackInfo2.displayId = displayId;
+        RootTaskInfo taskInfo2 = createTask(2, /* isVisible= */ true);
+        taskInfo2.childTaskIds = new int[] { 111, 222, 333, taskId };
+        taskInfo2.displayId = displayId;
 
-        List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2);
+        List<RootTaskInfo> taskInfoList = Arrays.asList(taskInfo1, taskInfo2);
 
-        when(mActivityTaskManager.getAllStackInfos()).thenReturn(stackInfoList);
-        when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(true);
+        when(mActivityTaskManager.getAllRootTaskInfos()).thenReturn(taskInfoList);
+        when(mSideLoadedAppDetector.isSafe(taskInfo2)).thenReturn(true);
 
         mSideLoadedAppListener.onTaskCreated(taskId, componentName);
 
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo2);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo1);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo2);
 
         verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
         verify(mSideLoadedAppStateController, never()).onSafeTaskDisplayedOnDisplay(any());
@@ -109,23 +109,23 @@
         int displayId = 123;
         ComponentName componentName = new ComponentName(APP_PACKAGE_NAME, APP_CLASS_NAME);
 
-        StackInfo stackInfo1 = createTask(1, /* isVisible= */ true);
-        stackInfo1.taskIds = new int[] { 11, 22, 33 };
-        StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
-        stackInfo2.taskIds = new int[] { 111, 222, 333, taskId };
-        stackInfo2.displayId = displayId;
-        List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2);
+        RootTaskInfo taskInfo1 = createTask(1, /* isVisible= */ true);
+        taskInfo1.childTaskIds = new int[] { 11, 22, 33 };
+        RootTaskInfo taskInfo2 = createTask(2, /* isVisible= */ true);
+        taskInfo2.childTaskIds = new int[] { 111, 222, 333, taskId };
+        taskInfo2.displayId = displayId;
+        List<RootTaskInfo> taskInfoList = Arrays.asList(taskInfo1, taskInfo2);
 
         Display display = createDisplay(displayId);
 
-        when(mActivityTaskManager.getAllStackInfos()).thenReturn(stackInfoList);
-        when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(false);
+        when(mActivityTaskManager.getAllRootTaskInfos()).thenReturn(taskInfoList);
+        when(mSideLoadedAppDetector.isSafe(taskInfo2)).thenReturn(false);
         when(mDisplayManager.getDisplay(displayId)).thenReturn(display);
 
         mSideLoadedAppListener.onTaskCreated(taskId, componentName);
 
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo2);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo1);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo2);
 
         verify(mSideLoadedAppStateController).onUnsafeTaskCreatedOnDisplay(display);
         verify(mSideLoadedAppStateController, never()).onSafeTaskDisplayedOnDisplay(any());
@@ -135,21 +135,21 @@
     @Test
     public void onTaskStackChanged_safeTask_callsSafeTaskDisplayed() throws Exception {
         Display display = createDisplay(123);
-        StackInfo stackInfo1 = createTask(1, /* isVisible= */ false);
-        StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
-        StackInfo stackInfo3 = createTask(3, /* isVisible= */ true);
-        List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2, stackInfo3);
+        RootTaskInfo taskInfo1 = createTask(1, /* isVisible= */ false);
+        RootTaskInfo taskInfo2 = createTask(2, /* isVisible= */ true);
+        RootTaskInfo taskInfo3 = createTask(3, /* isVisible= */ true);
+        List<RootTaskInfo> taskInfoList = Arrays.asList(taskInfo1, taskInfo2, taskInfo3);
 
-        when(mActivityTaskManager.getAllStackInfosOnDisplay(display.getDisplayId()))
-                .thenReturn(stackInfoList);
-        when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(true);
+        when(mActivityTaskManager.getAllRootTaskInfosOnDisplay(display.getDisplayId()))
+                .thenReturn(taskInfoList);
+        when(mSideLoadedAppDetector.isSafe(taskInfo2)).thenReturn(true);
         when(mDisplayManager.getDisplays()).thenReturn(new Display[] { display });
 
         mSideLoadedAppListener.onTaskStackChanged();
 
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo2);
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo3);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo1);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo2);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo3);
 
         verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
         verify(mSideLoadedAppStateController).onSafeTaskDisplayedOnDisplay(display);
@@ -159,21 +159,21 @@
     @Test
     public void onTaskStackChanged_unsafeTask_callsUnsafeTaskDisplayed() throws Exception {
         Display display = createDisplay(123);
-        StackInfo stackInfo1 = createTask(1, /* isVisible= */ false);
-        StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
-        StackInfo stackInfo3 = createTask(3, /* isVisible= */ true);
-        List<StackInfo> stackInfoList = Arrays.asList(stackInfo1, stackInfo2, stackInfo3);
+        RootTaskInfo taskInfo1 = createTask(1, /* isVisible= */ false);
+        RootTaskInfo taskInfo2 = createTask(2, /* isVisible= */ true);
+        RootTaskInfo taskInfo3 = createTask(3, /* isVisible= */ true);
+        List<RootTaskInfo> taskInfoList = Arrays.asList(taskInfo1, taskInfo2, taskInfo3);
 
-        when(mActivityTaskManager.getAllStackInfosOnDisplay(display.getDisplayId()))
-                .thenReturn(stackInfoList);
-        when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(false);
+        when(mActivityTaskManager.getAllRootTaskInfosOnDisplay(display.getDisplayId()))
+                .thenReturn(taskInfoList);
+        when(mSideLoadedAppDetector.isSafe(taskInfo2)).thenReturn(false);
         when(mDisplayManager.getDisplays()).thenReturn(new Display[] { display });
 
         mSideLoadedAppListener.onTaskStackChanged();
 
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo2);
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo3);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo1);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo2);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo3);
 
         verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
         verify(mSideLoadedAppStateController, never()).onSafeTaskDisplayedOnDisplay(any());
@@ -183,40 +183,40 @@
     @Test
     public void onTaskStackChanged_multiDisplay_callsTasksDisplayed() throws Exception {
         Display display1 = createDisplay(1);
-        StackInfo stackInfo1 = createTask(1, /* isVisible= */ false);
-        StackInfo stackInfo2 = createTask(2, /* isVisible= */ true);
-        StackInfo stackInfo3 = createTask(3, /* isVisible= */ true);
-        List<StackInfo> display1Stack = Arrays.asList(stackInfo1, stackInfo2, stackInfo3);
+        RootTaskInfo taskInfo1 = createTask(1, /* isVisible= */ false);
+        RootTaskInfo taskInfo2 = createTask(2, /* isVisible= */ true);
+        RootTaskInfo taskInfo3 = createTask(3, /* isVisible= */ true);
+        List<RootTaskInfo> display1Tasks = Arrays.asList(taskInfo1, taskInfo2, taskInfo3);
 
         Display display2 = createDisplay(2);
-        StackInfo stackInfo4 = createTask(4, /* isVisible= */ true);
-        List<StackInfo> display2Stack = Collections.singletonList(stackInfo4);
+        RootTaskInfo taskInfo4 = createTask(4, /* isVisible= */ true);
+        List<RootTaskInfo> display2Tasks = Collections.singletonList(taskInfo4);
 
         Display display3 = createDisplay(3);
-        StackInfo stackInfo5 = createTask(5, /* isVisible= */ true);
-        List<StackInfo> display3Stack = Collections.singletonList(stackInfo5);
+        RootTaskInfo taskInfo5 = createTask(5, /* isVisible= */ true);
+        List<RootTaskInfo> display3Tasks = Collections.singletonList(taskInfo5);
 
-        when(mActivityTaskManager.getAllStackInfosOnDisplay(display1.getDisplayId()))
-                .thenReturn(display1Stack);
-        when(mActivityTaskManager.getAllStackInfosOnDisplay(display2.getDisplayId()))
-                .thenReturn(display2Stack);
-        when(mActivityTaskManager.getAllStackInfosOnDisplay(display3.getDisplayId()))
-                .thenReturn(display3Stack);
+        when(mActivityTaskManager.getAllRootTaskInfosOnDisplay(display1.getDisplayId()))
+                .thenReturn(display1Tasks);
+        when(mActivityTaskManager.getAllRootTaskInfosOnDisplay(display2.getDisplayId()))
+                .thenReturn(display2Tasks);
+        when(mActivityTaskManager.getAllRootTaskInfosOnDisplay(display3.getDisplayId()))
+                .thenReturn(display3Tasks);
 
-        when(mSideLoadedAppDetector.isSafe(stackInfo2)).thenReturn(true);
-        when(mSideLoadedAppDetector.isSafe(stackInfo4)).thenReturn(false);
-        when(mSideLoadedAppDetector.isSafe(stackInfo5)).thenReturn(true);
+        when(mSideLoadedAppDetector.isSafe(taskInfo2)).thenReturn(true);
+        when(mSideLoadedAppDetector.isSafe(taskInfo4)).thenReturn(false);
+        when(mSideLoadedAppDetector.isSafe(taskInfo5)).thenReturn(true);
 
         when(mDisplayManager.getDisplays())
                 .thenReturn(new Display[] { display1, display2, display3});
 
         mSideLoadedAppListener.onTaskStackChanged();
 
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo1);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo2);
-        verify(mSideLoadedAppDetector, never()).isSafe(stackInfo3);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo4);
-        verify(mSideLoadedAppDetector).isSafe(stackInfo5);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo1);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo2);
+        verify(mSideLoadedAppDetector, never()).isSafe(taskInfo3);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo4);
+        verify(mSideLoadedAppDetector).isSafe(taskInfo5);
 
         verify(mSideLoadedAppStateController, never()).onUnsafeTaskCreatedOnDisplay(any());
         verify(mSideLoadedAppStateController).onSafeTaskDisplayedOnDisplay(display1);
@@ -234,10 +234,10 @@
                 DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
 
-    private StackInfo createTask(int id, boolean isVisible) {
-        StackInfo stackInfo = new StackInfo();
-        stackInfo.stackId = id;
-        stackInfo.visible = isVisible;
-        return stackInfo;
+    private RootTaskInfo createTask(int id, boolean isVisible) {
+        RootTaskInfo taskInfo = new RootTaskInfo();
+        taskInfo.taskId = id;
+        taskInfo.visible = isVisible;
+        return taskInfo;
     }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java
index 8f9e56e..ac7edd3 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java
@@ -109,6 +109,13 @@
     }
 
     @Test
+    public void removeAll_withNoRegisteredListener_doesNotUnregister() {
+        mUserNameViewController.removeAll();
+
+        verifyZeroInteractions(mCarUserManager);
+    }
+
+    @Test
     public void userLifecycleListener_onUserSwitchLifecycleEvent_updatesUserNameView() {
         ArgumentCaptor<CarUserManager.UserLifecycleListener> userLifecycleListenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarUserManager.UserLifecycleListener.class);
diff --git a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml
index 47eeb83..2b9fa41 100644
--- a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml
@@ -2,7 +2,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="keyguard_description" msgid="8582605799129954556">"Geef je wachtwoord op en ga door naar \'Dynamische systeemupdates\'"</string>
-    <string name="notification_install_completed" msgid="6252047868415172643">"Dynamisch systeem is gereed. Start je apparaat opnieuw op om het te gebruiken."</string>
+    <string name="notification_install_completed" msgid="6252047868415172643">"Dynamisch systeem is klaar. Start je apparaat opnieuw op om het te gebruiken."</string>
     <string name="notification_install_inprogress" msgid="7383334330065065017">"Installatie wordt uitgevoerd"</string>
     <string name="notification_install_failed" msgid="4066039210317521404">"Installatie mislukt"</string>
     <string name="notification_image_validation_failed" msgid="2720357826403917016">"Valideren van afbeelding mislukt. Installatie afbreken."</string>
diff --git a/packages/InputDevices/res/raw/keyboard_layout_czech_qwerty.kcm b/packages/InputDevices/res/raw/keyboard_layout_czech_qwerty.kcm
new file mode 100644
index 0000000..457d4da
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_czech_qwerty.kcm
@@ -0,0 +1,365 @@
+# 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.
+
+#
+# Czech (EU - qwerty) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              ';'
+    base:                               ';'
+    shift:                              '\u00b0'
+    ralt:                               '\u0060'
+    shift+ralt:                         '\u007e'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '+'
+    shift:                              '1'
+    ralt:                               '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '\u011b'
+    capslock:                           '\u011a'
+    shift:                              '2'
+    ralt:                               '@'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '\u0161'
+    capslock:                           '\u0160'
+    shift:                              '3'
+    ralt:                               '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '\u010d'
+    capslock:                           '\u010c'
+    shift:                              '4'
+    ralt:                               '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '\u0159'
+    capslock:                           '\u0158'
+    shift:                              '5'
+    ralt:                               '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '\u017e'
+    capslock:                           '\u017d'
+    shift:                              '6'
+    ralt:                               '^'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '\u00fd'
+    capslock:                           '\u00dd'
+    shift:                              '7'
+    ralt:                               '&'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '\u00e1'
+    capslock:                           '\u00c1'
+    shift:                              '8'
+    ralt:                               '*'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '\u00ed'
+    capslock:                           '\u00cd'
+    shift:                              '9'
+    ralt:                               '('
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '\u00e9'
+    capslock:                           '\u00c9'
+    shift:                              '0'
+    ralt:                               ')'
+}
+
+key MINUS {
+    label:                              '='
+    base:                               '='
+    shift:                              '%'
+    ralt:                               '-'
+    ralt+shift:                         '_'
+}
+
+key EQUALS {
+    label:                              '\u00b4'
+    base:                               '\u0301'
+    shift:                              '\u030c'
+    ralt:                               '='
+    ralt+shift:                         '+'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u00fa'
+    base:                               '\u00fa'
+    capslock:                           '\u00da'
+    shift:                              '/'
+    ralt:                               '['
+    ralt+shift:                         '{'
+}
+
+key RIGHT_BRACKET {
+    label:                              ')'
+    base:                               ')'
+    shift:                              '('
+    ralt:                               ']'
+    ralt+shift:                         '}'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              '\u016f'
+    base:                               '\u016f'
+    capslock:                           '\u016e'
+    shift:                              '"'
+    ralt:                               ';'
+    ralt+shift:                         ':'
+}
+
+key APOSTROPHE {
+    label:                              '\u00a7'
+    base:                               '\u00a7'
+    shift:                              '!'
+    ralt:                               '\u00a4'
+    ralt+shift:                         '\u005e'
+}
+
+key BACKSLASH {
+    label:                              '\u0308'
+    base:                               '\u0308'
+    shift:                              '\''
+    ralt:                               '\\'
+    ralt+shift:                         '|'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '\\'
+    base:                               '\\'
+    shift:                              '|'
+    ralt:                               '\u00df'
+    shift+ralt:                         '\u02dd'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+    ralt:                               '@'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+    ralt:                               '\u00b5'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '?'
+    ralt:                               '<'
+    shift+ralt:                         '\u00d7'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              ':'
+    ralt:                               '>'
+    shift+ralt:                         '\u00f7'
+}
+
+key SLASH {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+    ralt:                               '/'
+    ralt+shift:                         '?'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 9878146..e95a159 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -75,6 +75,9 @@
     <!-- Czech keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_czech">Czech</string>
 
+    <!-- Czech qwerty keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_czech_qwerty">Czech QWERTY style</string>
+
     <!-- Estonian keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_estonian">Estonian</string>
 
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 0d7a13b..aa599ae 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -92,6 +92,10 @@
             android:label="@string/keyboard_layout_czech"
             android:keyboardLayout="@raw/keyboard_layout_czech" />
 
+    <keyboard-layout android:name="keyboard_layout_czech_qwerty"
+            android:label="@string/keyboard_layout_czech_qwerty"
+            android:keyboardLayout="@raw/keyboard_layout_czech_qwerty" />
+
     <keyboard-layout android:name="keyboard_layout_estonian"
             android:label="@string/keyboard_layout_estonian"
             android:keyboardLayout="@raw/keyboard_layout_estonian" />
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index 108c86f..d3a9589 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="7488448184431507488">"Pakket-installatie"</string>
     <string name="install" msgid="711829760615509273">"Installeren"</string>
-    <string name="done" msgid="6632441120016885253">"Gereed"</string>
+    <string name="done" msgid="6632441120016885253">"Klaar"</string>
     <string name="cancel" msgid="1018267193425558088">"Annuleren"</string>
     <string name="installing" msgid="4921993079741206516">"Installeren…"</string>
     <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeren…"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 8f71509..7f6237d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -212,7 +212,7 @@
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Адладка па Wi-Fi"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Каб праглядаць і выкарыстоўваць даступныя прылады, уключыце адладку па Wi-Fi"</string>
     <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Спалучыць прыладу з дапамогай QR-кода"</string>
-    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Спалучаць новыя прылады з дапамогай сканера QR-кода"</string>
+    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Спалучаць новыя прылады з дапамогай сканера QR-кодаў"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Спалучыць прыладу з дапамогай кода спалучэння"</string>
     <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Спалучаць новыя прылады з дапамогай шасцізначнага кода"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"Спалучаныя прылады"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 7c2a0fd..775cd15 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -187,7 +187,7 @@
     <item msgid="97587758561106269">"Isključeno"</item>
     <item msgid="7126170197336963369">"Međuspremnici svih zapisnika"</item>
     <item msgid="7167543126036181392">"Međuspremnici svih zapisnika osim radija"</item>
-    <item msgid="5135340178556563979">"samo međuspremnik zapisnika kernela"</item>
+    <item msgid="5135340178556563979">"samo međumemorija zapisnika kernela"</item>
   </string-array>
   <string-array name="window_animation_scale_entries">
     <item msgid="2675263395797191850">"Animacija isključena"</item>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index fbd8876..dd2aa3b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1777,6 +1777,9 @@
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
                 SecureSettingsProto.Accessibility.HIGH_TEXT_CONTRAST_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.FORCE_BOLD_TEXT,
+                SecureSettingsProto.FORCE_BOLD_TEXT);
+        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
                 SecureSettingsProto.Accessibility.LARGE_POINTER_ICON);
         dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 319b44c..190015c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -217,6 +217,9 @@
     <!-- Permission needed for CTS test - UnsupportedErrorDialogTests -->
     <uses-permission android:name="android.permission.RESET_APP_ERRORS" />
 
+    <!-- Permission needed for CTS test - CtsSystemUiTestCases:PipNotificationTests -->
+    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
+
     <!-- Permission needed to run keyguard manager tests in CTS -->
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
 
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 1c680bb..176035f 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -7,8 +7,7 @@
 
     static_libs: [
         "androidx.legacy_legacy-support-v4",
-        "setupcompat",
-        "setupdesign",
+        "setup-wizard-lib",
     ],
 
     resource_dirs: ["res"],
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
index e7368f3..873f6c5 100644
--- a/packages/SimAppDialog/AndroidManifest.xml
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -23,7 +23,7 @@
             android:name=".InstallCarrierAppActivity"
             android:exported="true"
             android:permission="android.permission.NETWORK_SETTINGS"
-            android:theme="@style/SudThemeGlif.Light">
+            android:theme="@style/SuwThemeGlif.Light">
         </activity>
     </application>
 </manifest>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 68113db..12f9bb6 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -14,17 +14,18 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.google.android.setupdesign.GlifLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_signal_cellular_alt_rounded"
-    app:sucHeaderText="@string/install_carrier_app_title">
+    app:suwHeaderText="@string/install_carrier_app_title"
+    app:suwFooter="@layout/install_carrier_app_footer">
 
     <LinearLayout
-        style="@style/SudContentFrame"
+        style="@style/SuwContentFrame"
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -32,12 +33,12 @@
 
         <TextView
             android:id="@+id/install_carrier_app_description"
-            style="@style/SudDescription.Glif"
+            style="@style/SuwDescription.Glif"
             android:text="@string/install_carrier_app_description_default"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
 
-        <com.google.android.setupdesign.view.FillContentLayout
+        <com.android.setupwizardlib.view.FillContentLayout
             android:id="@+id/illo_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -46,12 +47,12 @@
 
             <ImageView
                 android:src="@drawable/illo_sim_app_dialog"
-                style="@style/SudContentIllustration"
+                style="@style/SuwContentIllustration"
                 android:contentDescription="@string/install_carrier_app_image_content_description"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"/>
 
-        </com.google.android.setupdesign.view.FillContentLayout>
-    </LinearLayout>
+        </com.android.setupwizardlib.view.FillContentLayout>
+</LinearLayout>
 
-</com.google.android.setupdesign.GlifLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
new file mode 100644
index 0000000..10dcb77
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<com.android.setupwizardlib.view.ButtonBarLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/footer"
+    style="@style/SuwGlifButtonBar.Stackable"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Button
+        android:id="@+id/skip_button"
+        style="@style/SuwGlifButton.Secondary"
+        android:text="@string/install_carrier_app_defer_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/download_button"
+        style="@style/SuwGlifButton.Primary"
+        android:text="@string/install_carrier_app_download_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml
deleted file mode 100644
index 824e380..0000000
--- a/packages/SimAppDialog/res/values/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    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.
--->
-<resources>
-
-  <style name="SetupWizardPartnerResource">
-    <!-- Disable to use partner overlay theme for outside setupwizard flow. -->
-    <item name="sucUsePartnerResource">false</item>
-    <!-- Enable heavy theme style inside setupwizard flow. -->
-    <item name="sudUsePartnerHeavyTheme">true</item>
-  </style>
-
-</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 0b6f9bb..abe82a8 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -17,17 +17,14 @@
 
 import android.app.Activity;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.sysprop.SetupWizardProperties;
 import android.text.TextUtils;
 import android.view.View;
+import android.widget.Button;
 import android.widget.TextView;
 
-import com.google.android.setupcompat.template.FooterBarMixin;
-import com.google.android.setupcompat.template.FooterButton;
-import com.google.android.setupdesign.GlifLayout;
-import com.google.android.setupdesign.util.ThemeResolver;
+import com.android.setupwizardlib.util.WizardManagerHelper;
 
 /**
  * Activity that gives a user the choice to download the SIM app or defer until a later time
@@ -38,7 +35,7 @@
  * Can display the carrier app name if its passed into the intent with key
  * {@link #BUNDLE_KEY_CARRIER_NAME}
  */
-public class InstallCarrierAppActivity extends Activity {
+public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
     /**
      * Key for the carrier app name that will be displayed as the app to download.  If unset, a
      * default description will be used
@@ -53,33 +50,20 @@
     protected void onCreate(Bundle icicle) {
         // Setup theme for aosp/pixel
         setTheme(
-                new ThemeResolver.Builder()
-                        .setDefaultTheme(R.style.SudThemeGlifV3_Light)
-                        .build()
-                        .resolve(SetupWizardProperties.theme().orElse(""),
-                                /* suppressDayNight= */ false));
+                WizardManagerHelper.getThemeRes(
+                        SetupWizardProperties.theme().orElse(""),
+                        R.style.SuwThemeGlif_Light
+                )
+        );
 
         super.onCreate(icicle);
         setContentView(R.layout.install_carrier_app_activity);
 
-        GlifLayout layout = findViewById(R.id.setup_wizard_layout);
-        FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
-        mixin.setSecondaryButton(
-                new FooterButton.Builder(this)
-                        .setText(R.string.install_carrier_app_defer_action)
-                        .setListener(this::onSkipButtonClick)
-                        .setButtonType(FooterButton.ButtonType.SKIP)
-                        .setTheme(R.style.SudGlifButton_Secondary)
-                        .build());
+        Button notNowButton = findViewById(R.id.skip_button);
+        notNowButton.setOnClickListener(this);
 
-        mixin.setPrimaryButton(
-                new FooterButton.Builder(this)
-                        .setText(R.string.install_carrier_app_download_action)
-                        .setListener(this::onDownloadButtonClick)
-                        .setButtonType(FooterButton.ButtonType.OTHER)
-                        .setTheme(R.style.SudGlifButton_Primary)
-                        .build());
-
+        Button downloadButton = findViewById(R.id.download_button);
+        downloadButton.setOnClickListener(this);
 
         // Show/hide illo depending on whether one was provided in a resource overlay
         boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
@@ -98,17 +82,15 @@
     }
 
     @Override
-    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
-        theme.applyStyle(R.style.SetupWizardPartnerResource, true);
-        super.onApplyThemeResource(theme, resid, first);
-    }
-
-    protected void onSkipButtonClick(View view) {
-        finish(DEFER_RESULT);
-    }
-
-    protected void onDownloadButtonClick(View view) {
-        finish(DOWNLOAD_RESULT);
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.skip_button:
+                finish(DEFER_RESULT);
+                break;
+            case R.id.download_button:
+                finish(DOWNLOAD_RESULT);
+                break;
+        }
     }
 
     private void finish(int resultCode) {
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 211a27c..0a36dae 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1067,6 +1067,8 @@
     <string name="controls_added_tooltip" msgid="4842812921719153085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎Hold Power button to see new controls‎‏‎‎‏‎"</string>
     <string name="controls_menu_add" msgid="4447246119229920050">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎Add controls‎‏‎‎‏‎"</string>
     <string name="controls_menu_edit" msgid="890623986951347062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎Edit controls‎‏‎‎‏‎"</string>
-    <string name="one_handed_tutorial_title" msgid="6312198435090726656">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎Using one-handed mode‎‏‎‎‏‎"</string>
-    <string name="one_handed_tutorial_description" msgid="7674850272340517174">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎To exit, swipe up from the bottom of the screen or tap anywhere above the app‎‏‎‎‏‎"</string>
+    <!-- no translation found for one_handed_tutorial_title (6312198435090726656) -->
+    <skip />
+    <!-- no translation found for one_handed_tutorial_description (7674850272340517174) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 4e9347c..79c2fbd 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -395,7 +395,7 @@
     <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Conectado (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string>
     <string name="quick_settings_connecting" msgid="2381969772953268809">"Conectando..."</string>
     <string name="quick_settings_tethering_label" msgid="5257299852322475780">"Compartir conexión"</string>
-    <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Zona Wi-Fi"</string>
+    <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punto de acceso"</string>
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activando…"</string>
     <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ahorro de datos activado"</string>
     <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
@@ -660,7 +660,7 @@
     <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Ajustes rápidos, <xliff:g id="TITLE">%s</xliff:g>."</string>
-    <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Zona Wi-Fi"</string>
+    <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Punto de acceso"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index b02be5d..01d3fec 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -390,7 +390,7 @@
     <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Kleuren omkeren"</string>
     <string name="quick_settings_color_space_label" msgid="537528291083575559">"Modus voor kleurcorrectie"</string>
     <string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellingen"</string>
-    <string name="quick_settings_done" msgid="2163641301648855793">"Gereed"</string>
+    <string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"Verbonden"</string>
     <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Verbonden, batterij <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="2381969772953268809">"Verbinding maken…"</string>
@@ -692,7 +692,7 @@
     <string name="notification_channel_silenced" msgid="1995937493874511359">"Deze meldingen worden zonder geluid weergegeven"</string>
     <string name="notification_channel_unsilenced" msgid="94878840742161152">"Deze meldingen stellen je op de hoogte"</string>
     <string name="inline_blocking_helper" msgid="2891486013649543452">"Meestal sluit je deze meldingen. \nWil je ze blijven weergeven?"</string>
-    <string name="inline_done_button" msgid="6043094985588909584">"Gereed"</string>
+    <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string>
     <string name="inline_keep_showing" msgid="8736001253507073497">"Deze meldingen blijven weergeven?"</string>
     <string name="inline_stop_button" msgid="2453460935438696090">"Meldingen stoppen"</string>
@@ -747,7 +747,7 @@
     <string name="notification_channel_switch_accessibility" msgid="8979885820432540252">"Meldingen van dit kanaal toestaan"</string>
     <string name="notification_more_settings" msgid="4936228656989201793">"Meer instellingen"</string>
     <string name="notification_app_settings" msgid="8963648463858039377">"Aanpassen"</string>
-    <string name="notification_done" msgid="6215117625922713976">"Gereed"</string>
+    <string name="notification_done" msgid="6215117625922713976">"Klaar"</string>
     <string name="inline_undo" msgid="9026953267645116526">"Ongedaan maken"</string>
     <string name="demote" msgid="6225813324237153980">"Deze melding markeren als geen gesprek"</string>
     <string name="notification_conversation_favorite" msgid="1905240206975921907">"Belangrijk gesprek"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1c3fba2..ab09a96 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -560,9 +560,6 @@
     <!-- If the config font scale is >= this value, potentially adjust the number of columns-->
     <item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item>
 
-    <!-- Allow one handed to enable round corner -->
-    <bool name="config_one_handed_enable_round_corner">true</bool>
-
     <!-- Show a separate icon for low and high volume on the volume dialog -->
     <bool name="config_showLowMediaVolumeIcon">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 76c61fb..875fe14 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1366,8 +1366,4 @@
     <dimen name="config_rounded_mask_size">@*android:dimen/rounded_corner_radius</dimen>
     <dimen name="config_rounded_mask_size_top">@*android:dimen/rounded_corner_radius_top</dimen>
     <dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
-
-    <!-- One-Handed Mode -->
-    <!-- Threshold for dragging distance to enable one-handed mode -->
-    <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e58bf3b..cca70f9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2799,9 +2799,4 @@
     <string name="udfps_hbm_enable_command" translatable="false"></string>
     <!-- Device-specific payload for disabling the high-brightness mode -->
     <string name="udfps_hbm_disable_command" translatable="false"></string>
-
-    <!-- One-Handed Tutorial title [CHAR LIMIT=60] -->
-    <string name="one_handed_tutorial_title">Using one-handed mode</string>
-    <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
-    <string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index d64bf77..067ac9e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -153,12 +153,9 @@
         }
     }
 
+    @Deprecated
     public void setPipVisibility(final boolean visible) {
-        try {
-            WindowManagerGlobal.getWindowManagerService().setPipVisibility(visible);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to reach window manager", e);
-        }
+        // To be removed
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 57b3761..08e9cf6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -16,32 +16,11 @@
 
 package com.android.keyguard;
 
-import android.app.ActivityManager;
 import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.media.AudioManager;
-import android.os.SystemClock;
-import android.service.trust.TrustAgentService;
-import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
 import android.widget.FrameLayout;
 
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-
-import java.io.File;
-
 /**
  * Base class for keyguard view.  {@link #reset} is where you should
  * reset the state of your view.  Use the {@link KeyguardViewCallback} via
@@ -51,59 +30,10 @@
  * Handles intercepting of media keys that still work when the keyguard is
  * showing.
  */
-public class KeyguardHostView extends FrameLayout implements SecurityCallback {
+public class KeyguardHostView extends FrameLayout {
 
-    private AudioManager mAudioManager;
-    private TelephonyManager mTelephonyManager = null;
     protected ViewMediatorCallback mViewMediatorCallback;
-    protected LockPatternUtils mLockPatternUtils;
-    private OnDismissAction mDismissAction;
-    private Runnable mCancelAction;
 
-    private final KeyguardUpdateMonitorCallback mUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-
-        @Override
-        public void onUserSwitchComplete(int userId) {
-            getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);
-        }
-
-        @Override
-        public void onTrustGrantedWithFlags(int flags, int userId) {
-            if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
-            if (!isAttachedToWindow()) return;
-            boolean bouncerVisible = isVisibleToUser();
-            boolean initiatedByUser =
-                    (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
-            boolean dismissKeyguard =
-                    (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
-
-            if (initiatedByUser || dismissKeyguard) {
-                if (mViewMediatorCallback.isScreenOn() && (bouncerVisible || dismissKeyguard)) {
-                    if (!bouncerVisible) {
-                        // The trust agent dismissed the keyguard without the user proving
-                        // that they are present (by swiping up to show the bouncer). That's fine if
-                        // the user proved presence via some other way to the trust agent.
-                        Log.i(TAG, "TrustAgent dismissed Keyguard.");
-                    }
-                    dismiss(false /* authenticated */, userId,
-                            /* bypassSecondaryLockScreen */ false);
-                } else {
-                    mViewMediatorCallback.playTrustedSound();
-                }
-            }
-        }
-    };
-
-    // Whether the volume keys should be handled by keyguard. If true, then
-    // they will be handled here for specific media types such as music, otherwise
-    // the audio service will bring up the volume dialog.
-    private static final boolean KEYGUARD_MANAGES_VOLUME = false;
-    public static final boolean DEBUG = KeyguardConstants.DEBUG;
-    private static final String TAG = "KeyguardViewBase";
-
-    @VisibleForTesting
-    protected KeyguardSecurityContainer mSecurityContainer;
 
     public KeyguardHostView(Context context) {
         this(context, null);
@@ -111,7 +41,6 @@
 
     public KeyguardHostView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateCallback);
     }
 
     @Override
@@ -122,337 +51,7 @@
         }
     }
 
-    /**
-     * Sets an action to run when keyguard finishes.
-     *
-     * @param action
-     */
-    public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) {
-        if (mCancelAction != null) {
-            mCancelAction.run();
-            mCancelAction = null;
-        }
-        mDismissAction = action;
-        mCancelAction = cancelAction;
-    }
-
-    public boolean hasDismissActions() {
-        return mDismissAction != null || mCancelAction != null;
-    }
-
-    public void cancelDismissAction() {
-        setOnDismissAction(null, null);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        mSecurityContainer =
-                findViewById(R.id.keyguard_security_container);
-        mLockPatternUtils = new LockPatternUtils(mContext);
-        mSecurityContainer.setLockPatternUtils(mLockPatternUtils);
-        mSecurityContainer.setSecurityCallback(this);
-        mSecurityContainer.showPrimarySecurityScreen(false);
-    }
-
-    /**
-     * Called when the view needs to be shown.
-     */
-    public void showPrimarySecurityScreen() {
-        if (DEBUG) Log.d(TAG, "show()");
-        mSecurityContainer.showPrimarySecurityScreen(false);
-    }
-
-    public KeyguardSecurityView getCurrentSecurityView() {
-        return mSecurityContainer != null ? mSecurityContainer.getCurrentSecurityView() : null;
-    }
-
-    /**
-     * Show a string explaining why the security view needs to be solved.
-     *
-     * @param reason a flag indicating which string should be shown, see
-     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
-     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
-     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
-     *               {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
-     */
-    public void showPromptReason(int reason) {
-        mSecurityContainer.showPromptReason(reason);
-    }
-
-    public void showMessage(CharSequence message, ColorStateList colorState) {
-        mSecurityContainer.showMessage(message, colorState);
-    }
-
-    public void showErrorMessage(CharSequence message) {
-        showMessage(message, Utils.getColorError(mContext));
-    }
-
-    /**
-     * Dismisses the keyguard by going to the next screen or making it gone.
-     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
-     * @return True if the keyguard is done.
-     */
-    public boolean dismiss(int targetUserId) {
-        return dismiss(false, targetUserId, false);
-    }
-
-    public boolean handleBackKey() {
-        if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) {
-            mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser());
-            return true;
-        }
-        return false;
-    }
-
-    protected KeyguardSecurityContainer getSecurityContainer() {
-        return mSecurityContainer;
-    }
-
-    @Override
-    public boolean dismiss(boolean authenticated, int targetUserId,
-            boolean bypassSecondaryLockScreen) {
-        return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
-                bypassSecondaryLockScreen);
-    }
-
-    /**
-     * Authentication has happened and it's time to dismiss keyguard. This function
-     * should clean up and inform KeyguardViewMediator.
-     *
-     * @param strongAuth whether the user has authenticated with strong authentication like
-     *                   pattern, password or PIN but not by trust agents or fingerprint
-     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
-     */
-    @Override
-    public void finish(boolean strongAuth, int targetUserId) {
-        // If there's a pending runnable because the user interacted with a widget
-        // and we're leaving keyguard, then run it.
-        boolean deferKeyguardDone = false;
-        if (mDismissAction != null) {
-            deferKeyguardDone = mDismissAction.onDismiss();
-            mDismissAction = null;
-            mCancelAction = null;
-        }
-        if (mViewMediatorCallback != null) {
-            if (deferKeyguardDone) {
-                mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
-            } else {
-                mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
-            }
-        }
-    }
-
-    @Override
-    public void reset() {
-        mViewMediatorCallback.resetKeyguard();
-    }
-
-    @Override
-    public void onCancelClicked() {
-        mViewMediatorCallback.onCancelClicked();
-    }
-
-    public void resetSecurityContainer() {
-        mSecurityContainer.reset();
-    }
-
-    @Override
-    public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
-        if (mViewMediatorCallback != null) {
-            mViewMediatorCallback.setNeedsInput(needsInput);
-        }
-    }
-
-    public CharSequence getAccessibilityTitleForCurrentMode() {
-        return mSecurityContainer.getTitle();
-    }
-
-    public void userActivity() {
-        if (mViewMediatorCallback != null) {
-            mViewMediatorCallback.userActivity();
-        }
-    }
-
-    /**
-     * Called when the Keyguard is not actively shown anymore on the screen.
-     */
-    public void onPause() {
-        if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
-                Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
-        mSecurityContainer.showPrimarySecurityScreen(true);
-        mSecurityContainer.onPause();
-        clearFocus();
-    }
-
-    /**
-     * Called when the Keyguard is actively shown on the screen.
-     */
-    public void onResume() {
-        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
-        mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
-        requestFocus();
-    }
-
-    /**
-     * Starts the animation when the Keyguard gets shown.
-     */
-    public void startAppearAnimation() {
-        mSecurityContainer.startAppearAnimation();
-    }
-
-    public void startDisappearAnimation(Runnable finishRunnable) {
-        if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) {
-            finishRunnable.run();
-        }
-    }
-
-    /**
-     * Called before this view is being removed.
-     */
-    public void cleanUp() {
-        getSecurityContainer().onPause();
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (interceptMediaKey(event)) {
-            return true;
-        }
-        return super.dispatchKeyEvent(event);
-    }
-
-    /**
-     * Allows the media keys to work when the keyguard is showing.
-     * The media keys should be of no interest to the actual keyguard view(s),
-     * so intercepting them here should not be of any harm.
-     * @param event The key event
-     * @return whether the event was consumed as a media key.
-     */
-    public boolean interceptMediaKey(KeyEvent event) {
-        final int keyCode = event.getKeyCode();
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_MEDIA_PLAY:
-                case KeyEvent.KEYCODE_MEDIA_PAUSE:
-                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
-                     * in-call to avoid music playback */
-                    if (mTelephonyManager == null) {
-                        mTelephonyManager = (TelephonyManager) getContext().getSystemService(
-                                Context.TELEPHONY_SERVICE);
-                    }
-                    if (mTelephonyManager != null &&
-                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
-                        return true;  // suppress key event
-                    }
-                case KeyEvent.KEYCODE_MUTE:
-                case KeyEvent.KEYCODE_HEADSETHOOK:
-                case KeyEvent.KEYCODE_MEDIA_STOP:
-                case KeyEvent.KEYCODE_MEDIA_NEXT:
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-                case KeyEvent.KEYCODE_MEDIA_REWIND:
-                case KeyEvent.KEYCODE_MEDIA_RECORD:
-                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
-                    handleMediaKeyEvent(event);
-                    return true;
-                }
-
-                case KeyEvent.KEYCODE_VOLUME_UP:
-                case KeyEvent.KEYCODE_VOLUME_DOWN:
-                case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                    if (KEYGUARD_MANAGES_VOLUME) {
-                        synchronized (this) {
-                            if (mAudioManager == null) {
-                                mAudioManager = (AudioManager) getContext().getSystemService(
-                                        Context.AUDIO_SERVICE);
-                            }
-                        }
-                        // Volume buttons should only function for music (local or remote).
-                        // TODO: Actually handle MUTE.
-                        mAudioManager.adjustSuggestedStreamVolume(
-                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                                        ? AudioManager.ADJUST_RAISE
-                                        : AudioManager.ADJUST_LOWER /* direction */,
-                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
-                        // Don't execute default volume behavior
-                        return true;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-        } else if (event.getAction() == KeyEvent.ACTION_UP) {
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_MUTE:
-                case KeyEvent.KEYCODE_HEADSETHOOK:
-                case KeyEvent.KEYCODE_MEDIA_PLAY:
-                case KeyEvent.KEYCODE_MEDIA_PAUSE:
-                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                case KeyEvent.KEYCODE_MEDIA_STOP:
-                case KeyEvent.KEYCODE_MEDIA_NEXT:
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-                case KeyEvent.KEYCODE_MEDIA_REWIND:
-                case KeyEvent.KEYCODE_MEDIA_RECORD:
-                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
-                    handleMediaKeyEvent(event);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private void handleMediaKeyEvent(KeyEvent keyEvent) {
-        synchronized (this) {
-            if (mAudioManager == null) {
-                mAudioManager = (AudioManager) getContext().getSystemService(
-                        Context.AUDIO_SERVICE);
-            }
-        }
-        mAudioManager.dispatchMediaKeyEvent(keyEvent);
-    }
-
-    /**
-     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
-     * some cases where we wish to disable it, notably when the menu button placement or technology
-     * is prone to false positives.
-     *
-     * @return true if the menu key should be enabled
-     */
-    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
-    public boolean shouldEnableMenuKey() {
-        final Resources res = getResources();
-        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
-        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
-        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
-        return !configDisabled || isTestHarness || fileOverride;
-    }
-
     public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
         mViewMediatorCallback = viewMediatorCallback;
-        // Update ViewMediator with the current input method requirements
-        mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput());
-    }
-
-    public void setLockPatternUtils(LockPatternUtils utils) {
-        mLockPatternUtils = utils;
-        mSecurityContainer.setLockPatternUtils(utils);
-    }
-
-    public SecurityMode getSecurityMode() {
-        return mSecurityContainer.getSecurityMode();
-    }
-
-    public SecurityMode getCurrentSecurityMode() {
-        return mSecurityContainer.getCurrentSecurityMode();
-    }
-
-    /**
-     * When bouncer was visible and is starting to become hidden.
-     */
-    public void onStartingToHide() {
-        mSecurityContainer.onStartingToHide();
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
new file mode 100644
index 0000000..7aabb17
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -0,0 +1,460 @@
+/*
+ * 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.keyguard;
+
+import android.app.ActivityManager;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.os.SystemClock;
+import android.service.trust.TrustAgentService;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.view.ViewTreeObserver;
+
+import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.util.ViewController;
+
+import java.io.File;
+
+import javax.inject.Inject;
+
+/** Controller for a {@link KeyguardHostView}. */
+@KeyguardBouncerScope
+public class KeyguardHostViewController extends ViewController<KeyguardHostView> {
+    private static final String TAG = "KeyguardViewBase";
+    public static final boolean DEBUG = KeyguardConstants.DEBUG;
+    // Whether the volume keys should be handled by keyguard. If true, then
+    // they will be handled here for specific media types such as music, otherwise
+    // the audio service will bring up the volume dialog.
+    private static final boolean KEYGUARD_MANAGES_VOLUME = false;
+
+    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
+
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+    private final TelephonyManager mTelephonyManager;
+    private final ViewMediatorCallback mViewMediatorCallback;
+    private final AudioManager mAudioManager;
+
+    private ActivityStarter.OnDismissAction mDismissAction;
+    private Runnable mCancelAction;
+
+    private final KeyguardUpdateMonitorCallback mUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onUserSwitchComplete(int userId) {
+                    mKeyguardSecurityContainerController.showPrimarySecurityScreen(
+                            false /* turning off */);
+                }
+
+                @Override
+                public void onTrustGrantedWithFlags(int flags, int userId) {
+                    if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
+                    boolean bouncerVisible = mView.isVisibleToUser();
+                    boolean initiatedByUser =
+                            (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
+                    boolean dismissKeyguard =
+                            (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
+
+                    if (initiatedByUser || dismissKeyguard) {
+                        if (mViewMediatorCallback.isScreenOn()
+                                && (bouncerVisible || dismissKeyguard)) {
+                            if (!bouncerVisible) {
+                                // The trust agent dismissed the keyguard without the user proving
+                                // that they are present (by swiping up to show the bouncer). That's
+                                // fine if the user proved presence via some other way to the trust
+                                //agent.
+                                Log.i(TAG, "TrustAgent dismissed Keyguard.");
+                            }
+                            mSecurityCallback.dismiss(false /* authenticated */, userId,
+                                    /* bypassSecondaryLockScreen */ false);
+                        } else {
+                            mViewMediatorCallback.playTrustedSound();
+                        }
+                    }
+                }
+            };
+
+    private final SecurityCallback mSecurityCallback = new SecurityCallback() {
+
+        @Override
+        public boolean dismiss(boolean authenticated, int targetUserId,
+                boolean bypassSecondaryLockScreen) {
+            return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+                    authenticated, targetUserId, bypassSecondaryLockScreen);
+        }
+
+        @Override
+        public void userActivity() {
+            mViewMediatorCallback.userActivity();
+        }
+
+        @Override
+        public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
+            mViewMediatorCallback.setNeedsInput(needsInput);
+        }
+
+        /**
+         * Authentication has happened and it's time to dismiss keyguard. This function
+         * should clean up and inform KeyguardViewMediator.
+         *
+         * @param strongAuth whether the user has authenticated with strong authentication like
+         *                   pattern, password or PIN but not by trust agents or fingerprint
+         * @param targetUserId a user that needs to be the foreground user at the dismissal
+         *                    completion.
+         */
+        @Override
+        public void finish(boolean strongAuth, int targetUserId) {
+            // If there's a pending runnable because the user interacted with a widget
+            // and we're leaving keyguard, then run it.
+            boolean deferKeyguardDone = false;
+            if (mDismissAction != null) {
+                deferKeyguardDone = mDismissAction.onDismiss();
+                mDismissAction = null;
+                mCancelAction = null;
+            }
+            if (mViewMediatorCallback != null) {
+                if (deferKeyguardDone) {
+                    mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
+                } else {
+                    mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
+                }
+            }
+        }
+
+        @Override
+        public void reset() {
+            mViewMediatorCallback.resetKeyguard();
+        }
+
+        @Override
+        public void onCancelClicked() {
+            mViewMediatorCallback.onCancelClicked();
+        }
+    };
+
+    private OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event);
+
+    @Inject
+    public KeyguardHostViewController(KeyguardHostView view,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardSecurityContainerController keyguardSecurityContainerController,
+            AudioManager audioManager,
+            TelephonyManager telephonyManager,
+            ViewMediatorCallback viewMediatorCallback) {
+        super(view);
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardSecurityContainerController = keyguardSecurityContainerController;
+        mAudioManager = audioManager;
+        mTelephonyManager = telephonyManager;
+        mViewMediatorCallback = viewMediatorCallback;
+    }
+
+    /** Initialize the Controller. */
+    public void init() {
+        super.init();
+        mView.setViewMediatorCallback(mViewMediatorCallback);
+        // Update ViewMediator with the current input method requirements
+        mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
+        mKeyguardSecurityContainerController.init();
+        mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
+        mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
+        mView.setOnKeyListener(mOnKeyListener);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mKeyguardUpdateMonitor.removeCallback(mUpdateCallback);
+        mView.setOnKeyListener(null);
+    }
+
+     /** Called before this view is being removed. */
+    public void cleanUp() {
+        mKeyguardSecurityContainerController.onPause();
+    }
+
+    public void resetSecurityContainer() {
+        mKeyguardSecurityContainerController.reset();
+    }
+
+    /**
+     * Dismisses the keyguard by going to the next screen or making it gone.
+     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+     * @return True if the keyguard is done.
+     */
+    public boolean dismiss(int targetUserId) {
+        return mSecurityCallback.dismiss(false, targetUserId, false);
+    }
+
+    /**
+     * Called when the Keyguard is actively shown on the screen.
+     */
+    public void onResume() {
+        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
+        mKeyguardSecurityContainerController.onResume(KeyguardSecurityView.SCREEN_ON);
+        mView.requestFocus();
+    }
+
+    public CharSequence getAccessibilityTitleForCurrentMode() {
+        return mKeyguardSecurityContainerController.getTitle();
+    }
+
+    /**
+     * Starts the animation when the Keyguard gets shown.
+     */
+    public void appear(int statusBarHeight) {
+        // We might still be collapsed and the view didn't have time to layout yet or still
+        // be small, let's wait on the predraw to do the animation in that case.
+        if (mView.getHeight() != 0 && mView.getHeight() != statusBarHeight) {
+            mKeyguardSecurityContainerController.startAppearAnimation();
+        } else {
+            mView.getViewTreeObserver().addOnPreDrawListener(
+                    new ViewTreeObserver.OnPreDrawListener() {
+                        @Override
+                        public boolean onPreDraw() {
+                            mView.getViewTreeObserver().removeOnPreDrawListener(this);
+                            mKeyguardSecurityContainerController.startAppearAnimation();
+                            return true;
+                        }
+                    });
+            mView.requestLayout();
+        }
+    }
+
+    /**
+     * Show a string explaining why the security view needs to be solved.
+     *
+     * @param reason a flag indicating which string should be shown, see
+     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
+     *               {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
+     */
+    public void showPromptReason(int reason) {
+        mKeyguardSecurityContainerController.showPromptReason(reason);
+    }
+
+    public void showMessage(CharSequence message, ColorStateList colorState) {
+        mKeyguardSecurityContainerController.showMessage(message, colorState);
+    }
+
+    public void showErrorMessage(CharSequence customMessage) {
+        showMessage(customMessage, Utils.getColorError(mView.getContext()));
+    }
+
+    /**
+     * Sets an action to run when keyguard finishes.
+     *
+     * @param action
+     */
+    public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
+        if (mCancelAction != null) {
+            mCancelAction.run();
+            mCancelAction = null;
+        }
+        mDismissAction = action;
+        mCancelAction = cancelAction;
+    }
+
+    public void cancelDismissAction() {
+        setOnDismissAction(null, null);
+    }
+
+    public void startDisappearAnimation(Runnable finishRunnable) {
+        if (!mKeyguardSecurityContainerController.startDisappearAnimation(finishRunnable)
+                && finishRunnable != null) {
+            finishRunnable.run();
+        }
+    }
+
+    /**
+     * Called when the Keyguard is not actively shown anymore on the screen.
+     */
+    public void onPause() {
+        if (DEBUG) {
+            Log.d(TAG, String.format("screen off, instance %s at %s",
+                    Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
+        }
+        mKeyguardSecurityContainerController.showPrimarySecurityScreen(true);
+        mKeyguardSecurityContainerController.onPause();
+        mView.clearFocus();
+    }
+
+    /**
+     * Called when the view needs to be shown.
+     */
+    public void showPrimarySecurityScreen() {
+        if (DEBUG) Log.d(TAG, "show()");
+        mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
+    }
+
+    public void setExpansion(float fraction) {
+        float alpha = MathUtils.map(KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
+        mView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
+        mView.setTranslationY(fraction * mView.getHeight());
+    }
+
+    /**
+     * When bouncer was visible and is starting to become hidden.
+     */
+    public void onStartingToHide() {
+        mKeyguardSecurityContainerController.onStartingToHide();
+    }
+
+    public boolean hasDismissActions() {
+        return mDismissAction != null || mCancelAction != null;
+    }
+
+    public SecurityMode getCurrentSecurityMode() {
+        return mKeyguardSecurityContainerController.getCurrentSecurityMode();
+    }
+
+    public int getTop() {
+        int top = mView.getTop();
+        // The password view has an extra top padding that should be ignored.
+        if (getCurrentSecurityMode() == SecurityMode.Password) {
+            View messageArea = mView.findViewById(R.id.keyguard_message_area);
+            top += messageArea.getTop();
+        }
+        return top;
+    }
+
+    public boolean handleBackKey() {
+        if (mKeyguardSecurityContainerController.getCurrentSecuritySelection()
+                != SecurityMode.None) {
+            mKeyguardSecurityContainerController.dismiss(
+                    false, KeyguardUpdateMonitor.getCurrentUser());
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
+     * some cases where we wish to disable it, notably when the menu button placement or technology
+     * is prone to false positives.
+     *
+     * @return true if the menu key should be enabled
+     */
+    public boolean shouldEnableMenuKey() {
+        final Resources res = mView.getResources();
+        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
+        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
+        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
+        return !configDisabled || isTestHarness || fileOverride;
+    }
+
+    /**
+     * Allows the media keys to work when the keyguard is showing.
+     * The media keys should be of no interest to the actual keyguard view(s),
+     * so intercepting them here should not be of any harm.
+     * @param event The key event
+     * @return whether the event was consumed as a media key.
+     */
+    public boolean interceptMediaKey(KeyEvent event) {
+        int keyCode = event.getKeyCode();
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
+                     * in-call to avoid music playback */
+                    if (mTelephonyManager != null &&
+                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
+                        return true;  // suppress key event
+                    }
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                    handleMediaKeyEvent(event);
+                    return true;
+                }
+
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                case KeyEvent.KEYCODE_VOLUME_MUTE: {
+                    if (KEYGUARD_MANAGES_VOLUME) {
+                        // Volume buttons should only function for music (local or remote).
+                        // TODO: Actually handle MUTE.
+                        mAudioManager.adjustSuggestedStreamVolume(
+                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                                        ? AudioManager.ADJUST_RAISE
+                                        : AudioManager.ADJUST_LOWER /* direction */,
+                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
+                        // Don't execute default volume behavior
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                    handleMediaKeyEvent(event);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+    private void handleMediaKeyEvent(KeyEvent keyEvent) {
+        mAudioManager.dispatchMediaKeyEvent(keyEvent);
+    }
+
+    public void finish(boolean strongAuth, int currentUser) {
+        mSecurityCallback.finish(strongAuth, currentUser);
+    }
+
+
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
new file mode 100644
index 0000000..f056bdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -0,0 +1,68 @@
+/*
+ * 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.keyguard;
+
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/** Controller for a {@link KeyguardMessageAreaController}. */
+public class KeyguardMessageAreaController extends ViewController<KeyguardMessageArea> {
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final ConfigurationController mConfigurationController;
+
+    private KeyguardMessageAreaController(KeyguardMessageArea view,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            ConfigurationController configurationController) {
+        super(view);
+
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mConfigurationController = configurationController;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        //mConfigurationController.addCallback();
+        //mKeyguardUpdateMonitor.registerCallback();
+    }
+
+    @Override
+    protected void onViewDetached() {
+        //mConfigurationController.removeCallback();
+        //mKeyguardUpdateMonitor.removeCallback();
+    }
+
+    /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */
+    public static class Factory {
+        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+        private final ConfigurationController mConfigurationController;
+
+        @Inject
+        public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
+                ConfigurationController configurationController) {
+            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+            mConfigurationController = configurationController;
+        }
+
+        /** Build a new {@link KeyguardMessageAreaController}. */
+        public KeyguardMessageAreaController create(KeyguardMessageArea view) {
+            return new KeyguardMessageAreaController(
+                    view, mKeyguardUpdateMonitor, mConfigurationController);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
new file mode 100644
index 0000000..5c125fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
@@ -0,0 +1,48 @@
+/*
+ * 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.keyguard;
+
+import android.view.ViewGroup;
+
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.keyguard.dagger.RootView;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+/** Controller for a {@link KeyguardBouncer}'s Root view. */
+@KeyguardBouncerScope
+public class KeyguardRootViewController extends ViewController<ViewGroup> {
+    @Inject
+    public KeyguardRootViewController(@RootView ViewGroup view) {
+        super(view);
+    }
+
+    public ViewGroup getView() {
+        return mView;
+    }
+
+    @Override
+    protected void onViewAttached() {
+
+    }
+
+    @Override
+    protected void onViewDetached() {
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
new file mode 100644
index 0000000..17f25bd08ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -0,0 +1,118 @@
+/*
+ * 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.keyguard;
+
+import android.content.res.ColorStateList;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/** Controller for {@link KeyguardSecurityContainer} */
+public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> {
+
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory;
+
+    @Inject
+    KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+            LockPatternUtils lockPatternUtils,
+            KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) {
+        super(view);
+        mLockPatternUtils = lockPatternUtils;
+        view.setLockPatternUtils(mLockPatternUtils);
+        mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+    }
+
+    @Override
+    protected void onViewAttached() {
+    }
+
+    @Override
+    protected void onViewDetached() {
+    }
+
+    /** */
+    public void onPause() {
+        mView.onPause();
+    }
+
+    public void showPrimarySecurityScreen(boolean turningOff) {
+        mView.showPrimarySecurityScreen(turningOff);
+    }
+
+    public void showPromptReason(int reason) {
+        mView.showPromptReason(reason);
+    }
+
+    public void showMessage(CharSequence message, ColorStateList colorState) {
+        mView.showMessage(message, colorState);
+    }
+
+    public SecurityMode getCurrentSecuritySelection() {
+        return mView.getCurrentSecuritySelection();
+    }
+
+    public void dismiss(boolean authenticated, int targetUserId) {
+        mView.dismiss(authenticated, targetUserId);
+    }
+
+    public void reset() {
+        mView.reset();
+    }
+
+    public CharSequence getTitle() {
+        return mView.getTitle();
+    }
+
+    public void onResume(int screenOn) {
+        mView.onResume(screenOn);
+    }
+
+    public void startAppearAnimation() {
+        mView.startAppearAnimation();
+    }
+
+    public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+        return mView.startDisappearAnimation(onFinishRunnable);
+    }
+
+    public void onStartingToHide() {
+        mView.onStartingToHide();
+    }
+
+    public void setSecurityCallback(SecurityCallback securityCallback) {
+        mView.setSecurityCallback(securityCallback);
+    }
+
+    public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
+            boolean bypassSecondaryLockScreen) {
+        return mView.showNextSecurityScreenOrFinish(
+                authenticated, targetUserId, bypassSecondaryLockScreen);
+    }
+
+    public boolean needsInput() {
+        return mView.needsInput();
+    }
+
+    public SecurityMode getCurrentSecurityMode() {
+        return mView.getCurrentSecurityMode();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
new file mode 100644
index 0000000..ef9ba19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
@@ -0,0 +1,58 @@
+/*
+ * 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.keyguard;
+
+import android.view.View;
+
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+
+/** Controller for a {@link KeyguardSecurityView}. */
+public class KeyguardSecurityViewController extends ViewController<View> {
+
+    private final KeyguardSecurityView mView;
+
+    private KeyguardSecurityViewController(KeyguardSecurityView view) {
+        super((View) view);
+        // KeyguardSecurityView isn't actually a View, so we need to track it ourselves.
+        mView = view;
+    }
+
+    @Override
+    protected void onViewAttached() {
+
+    }
+
+    @Override
+    protected void onViewDetached() {
+
+    }
+
+    /** Factory for a {@link KeyguardSecurityViewController}. */
+    public static class Factory {
+        @Inject
+        public Factory() {
+        }
+
+        /** Create a new {@link KeyguardSecurityViewController}. */
+        public KeyguardSecurityViewController create(KeyguardSecurityView view) {
+            return new KeyguardSecurityViewController(view);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1027329..76090f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -36,6 +36,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.app.UserSwitchObserver;
@@ -2783,7 +2784,7 @@
         @Override
         public void onTaskStackChangedBackground() {
             try {
-                ActivityManager.StackInfo info = ActivityTaskManager.getService().getStackInfo(
+                RootTaskInfo info = ActivityTaskManager.getService().getRootTaskInfo(
                         WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
                 if (info == null) {
                     return;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
index 84deaca..5160b7e 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
@@ -16,27 +16,27 @@
 
 package com.android.keyguard.dagger;
 
-import android.view.ViewGroup;
-
+import com.android.keyguard.KeyguardHostViewController;
+import com.android.keyguard.KeyguardRootViewController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 
-import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
  * Dagger Subcomponent for the {@link KeyguardBouncer}.
  */
-@Subcomponent
+@Subcomponent(modules = {KeyguardBouncerModule.class})
 @KeyguardBouncerScope
 public interface KeyguardBouncerComponent {
     /** Simple factory for {@link KeyguardBouncerComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        KeyguardBouncerComponent build(
-                @BindsInstance @ContainerView ViewGroup container,
-                @BindsInstance KeyguardBouncer.BouncerExpansionCallback bouncerExpansionCallback);
+        KeyguardBouncerComponent create();
     }
 
-    /** */
-    KeyguardBouncer createKeyguardBouncer();
+    /** Returns a {@link KeyguardRootViewController}. */
+    KeyguardRootViewController getKeyguardRootViewController();
+
+    /** Returns a {@link KeyguardHostViewController}. */
+    KeyguardHostViewController getKeyguardHostViewController();
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
new file mode 100644
index 0000000..b6010c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -0,0 +1,64 @@
+/*
+ * 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.keyguard.dagger;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.keyguard.KeyguardHostView;
+import com.android.keyguard.KeyguardMessageArea;
+import com.android.keyguard.KeyguardSecurityContainer;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Module to create and access view related to the {@link KeyguardBouncer}.
+ */
+@Module
+public interface KeyguardBouncerModule {
+    /** */
+    @Provides
+    @KeyguardBouncerScope
+    @RootView
+    static ViewGroup providesRootView(LayoutInflater layoutInflater) {
+        return (ViewGroup) layoutInflater.inflate(R.layout.keyguard_bouncer, null);
+    }
+
+    /** */
+    @Provides
+    @KeyguardBouncerScope
+    static KeyguardMessageArea providesKeyguardMessageArea(@RootView ViewGroup viewGroup) {
+        return viewGroup.findViewById(R.id.keyguard_message_area);
+    }
+
+    /** */
+    @Provides
+    @KeyguardBouncerScope
+    static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView) {
+        return rootView.findViewById(R.id.keyguard_host_view);
+    }
+
+    /** */
+    @Provides
+    @KeyguardBouncerScope
+    static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) {
+        return hostView.findViewById(R.id.keyguard_security_container);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java b/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
similarity index 96%
rename from packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
rename to packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
index e65f19d..5ebff09 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java
@@ -26,5 +26,5 @@
 @Qualifier
 @Documented
 @Retention(RUNTIME)
-public @interface ContainerView {
+public @interface RootView {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index a86a469..9f7358b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -17,9 +17,12 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.PathParser;
 import android.widget.ImageView;
@@ -30,6 +33,9 @@
 
 import java.util.EnumSet;
 
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
 /**
  * View that displays an adaptive icon with an app-badge and a dot.
  *
@@ -43,6 +49,8 @@
     public static final float WHITE_SCRIM_ALPHA = 0.54f;
     /** Same as value in Launcher3 IconShape */
     public static final int DEFAULT_PATH_SIZE = 100;
+    /** Same as value in Launcher3 BaseIconFactory */
+    private static final float ICON_BADGE_SCALE = 0.444f;
 
     /**
      * Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of
@@ -69,6 +77,7 @@
     private BubbleViewProvider mBubble;
 
     private int mBubbleBitmapSize;
+    private int mBubbleSize;
     private DotRenderer mDotRenderer;
     private DotRenderer.DrawParams mDrawParams;
     private boolean mOnLeft;
@@ -93,6 +102,7 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mBubbleBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
+        mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
         mDrawParams = new DotRenderer.DrawParams();
 
         Path iconPath = PathParser.createPathFromPathData(
@@ -108,7 +118,7 @@
      */
     public void setRenderedBubble(BubbleViewProvider bubble) {
         mBubble = bubble;
-        setImageBitmap(bubble.getBadgedImage());
+        showBadge();
         mDotColor = bubble.getDotColor();
         drawDot(bubble.getDotPath());
     }
@@ -161,14 +171,6 @@
     }
 
     /**
-     * Set whether the dot should appear on left or right side of the view.
-     */
-    void setDotOnLeft(boolean onLeft) {
-        mOnLeft = onLeft;
-        invalidate();
-    }
-
-    /**
      * @param iconPath The new icon path to use when calculating dot position.
      */
     void drawDot(Path iconPath) {
@@ -219,22 +221,29 @@
         return mDotColor;
     }
 
-    /** Sets the position of the 'new' dot, animating it out and back in if requested. */
-    void setDotPositionOnLeft(boolean onLeft, boolean animate) {
-        if (animate && onLeft != getDotOnLeft() && shouldDrawDot()) {
+    /** Sets the position of the dot and badge, animating them out and back in if requested. */
+    void animateDotBadgePositions(boolean onLeft) {
+        mOnLeft = onLeft;
+
+        if (onLeft != getDotOnLeft() && shouldDrawDot()) {
             animateDotScale(0f /* showDot */, () -> {
-                setDotOnLeft(onLeft);
+                invalidate();
                 animateDotScale(1.0f, null /* after */);
             });
-        } else {
-            setDotOnLeft(onLeft);
         }
+        // TODO animate badge
+        showBadge();
+
     }
 
-    boolean getDotPositionOnLeft() {
-        return getDotOnLeft();
+    /** Sets the position of the dot and badge. */
+    void setDotBadgeOnLeft(boolean onLeft) {
+        mOnLeft = onLeft;
+        invalidate();
+        showBadge();
     }
 
+
     /** Whether to draw the dot in onDraw(). */
     private boolean shouldDrawDot() {
         // Always render the dot if it's animating, since it could be animating out. Otherwise, show
@@ -276,4 +285,33 @@
                     }
                 }).start();
     }
+
+    void showBadge() {
+        Drawable badge = mBubble.getAppBadge();
+        if (badge == null) {
+            setImageBitmap(mBubble.getBubbleIcon());
+            return;
+        }
+        Canvas bubbleCanvas = new Canvas();
+        Bitmap noBadgeBubble = mBubble.getBubbleIcon();
+        Bitmap bubble = noBadgeBubble.copy(noBadgeBubble.getConfig(), /* isMutable */ true);
+
+        bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
+        bubbleCanvas.setBitmap(bubble);
+
+        final int badgeSize = (int) (ICON_BADGE_SCALE * mBubbleSize);
+        if (mOnLeft) {
+            badge.setBounds(0, mBubbleSize - badgeSize, badgeSize, mBubbleSize);
+        } else {
+            badge.setBounds(mBubbleSize - badgeSize, mBubbleSize - badgeSize,
+                    mBubbleSize, mBubbleSize);
+        }
+        badge.draw(bubbleCanvas);
+        bubbleCanvas.setBitmap(null);
+        setImageBitmap(bubble);
+    }
+
+    void hideBadge() {
+        setImageBitmap(mBubble.getBubbleIcon());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 62bc425..e6c1bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -41,7 +41,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
@@ -92,8 +91,9 @@
     }
 
     private FlyoutMessage mFlyoutMessage;
-    private Drawable mBadgedAppIcon;
-    private Bitmap mBadgedImage;
+    private Drawable mBadgeDrawable;
+    // Bitmap with no badge, no dot
+    private Bitmap mBubbleBitmap;
     private int mDotColor;
     private Path mDotPath;
     private int mFlags;
@@ -199,12 +199,13 @@
     }
 
     @Override
-    public Bitmap getBadgedImage() {
-        return mBadgedImage;
+    public Bitmap getBubbleIcon() {
+        return mBubbleBitmap;
     }
 
-    public Drawable getBadgedAppIcon() {
-        return mBadgedAppIcon;
+    @Override
+    public Drawable getAppBadge() {
+        return mBadgeDrawable;
     }
 
     @Override
@@ -340,8 +341,9 @@
         mAppName = info.appName;
         mFlyoutMessage = info.flyoutMessage;
 
-        mBadgedAppIcon = info.badgedAppIcon;
-        mBadgedImage = info.badgedBubbleImage;
+        mBadgeDrawable = info.badgeDrawable;
+        mBubbleBitmap = info.bubbleBitmap;
+
         mDotColor = info.dotColor;
         mDotPath = info.dotPath;
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index d017bc0..371e849 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -156,17 +156,4 @@
         canvas.setBitmap(null);
         return bitmap;
     }
-
-    /**
-     * Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
-     */
-    BitmapInfo getBubbleBitmap(Drawable bubble, BitmapInfo badge) {
-        BitmapInfo bubbleIconInfo = createBadgedIconBitmap(bubble,
-                null /* user */,
-                true /* shrinkNonAdaptiveIcons */);
-
-        badgeWithDrawable(bubbleIconInfo.icon,
-                new BitmapDrawable(mContext.getResources(), badge.icon));
-        return bubbleIconInfo;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
index 155b71b..6d3c2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -23,6 +23,7 @@
 import android.graphics.Path
 import android.graphics.drawable.AdaptiveIconDrawable
 import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
 import android.graphics.drawable.InsetDrawable
 import android.util.PathParser
 import android.util.TypedValue
@@ -36,8 +37,9 @@
     private val stack: BubbleStackView
 ) : BubbleViewProvider {
 
-    private var bitmap: Bitmap? = null
-    private var dotPath: Path? = null
+    private lateinit var bitmap : Bitmap
+    private lateinit var dotPath : Path
+
     private var bitmapSize = 0
     private var iconBitmapSize = 0
     private var dotColor = 0
@@ -80,40 +82,41 @@
         expandedView.updateDimensions()
     }
 
-    fun updateBtnTheme() {
+    private fun updateBtnTheme() {
         val res = context.resources
 
         // Set overflow button accent color, dot color
         val typedValue = TypedValue()
         context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
-
         val colorAccent = res.getColor(typedValue.resourceId)
-        overflowBtn.getDrawable()?.setTint(colorAccent)
+        overflowBtn.drawable?.setTint(colorAccent)
         dotColor = colorAccent
 
-        // Set button and activity background color
-        val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
-                == Configuration.UI_MODE_NIGHT_YES)
-        val bg = ColorDrawable(res.getColor(
-                if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
-
-        // Set button icon
         val iconFactory = BubbleIconFactory(context)
-        val fg = InsetDrawable(overflowBtn.getDrawable(),
-                bitmapSize - iconBitmapSize /* inset */)
-        bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
-                null /* user */, true /* shrinkNonAdaptiveIcons */).icon
 
-        // Set dot path
+        // Update bitmap
+        val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+            == Configuration.UI_MODE_NIGHT_YES)
+        val bg = ColorDrawable(res.getColor(
+            if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+        val fg = InsetDrawable(overflowBtn.drawable,
+            bitmapSize - iconBitmapSize /* inset */)
+        bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+            null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+        // Update dot path
         dotPath = PathParser.createPathFromPathData(
-                res.getString(com.android.internal.R.string.config_icon_mask))
+            res.getString(com.android.internal.R.string.config_icon_mask))
         val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
-                null /* outBounds */, null /* path */, null /* outMaskShape */)
+            null /* outBounds */, null /* path */, null /* outMaskShape */)
         val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
         val matrix = Matrix()
         matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
-                radius /* pivot y */)
-        dotPath?.transform(matrix)
+            radius /* pivot y */)
+        dotPath.transform(matrix)
+
+        // Attach BubbleOverflow to BadgedImageView
         overflowBtn.setRenderedBubble(this)
     }
 
@@ -129,7 +132,11 @@
         return dotColor
     }
 
-    override fun getBadgedImage(): Bitmap? {
+    override fun getAppBadge(): Drawable? {
+        return null
+    }
+
+    override fun getBubbleIcon(): Bitmap {
         return bitmap
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index c1b6882..55f9631 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -16,13 +16,6 @@
 
 package com.android.systemui.bubbles;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -99,6 +92,12 @@
 import java.util.List;
 import java.util.function.Consumer;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
 /**
  * Renders bubbles in a stack and handles animating expanded and collapsed states.
  */
@@ -658,13 +657,10 @@
                     mStackOnLeftOrWillBe =
                             mStackAnimationController.flingStackThenSpringToEdge(
                                     viewInitialX + dx, velX, velY) <= 0;
-
-                    updateBubbleZOrdersAndDotPosition(true /* animate */);
-
+                    updateBubbleIcons();
                     logBubbleEvent(null /* no bubble associated with bubble stack move */,
                             SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
                 }
-
                 mDismissView.hide();
             }
 
@@ -1468,8 +1464,7 @@
 
         // Set the dot position to the opposite of the side the stack is resting on, since the stack
         // resting slightly off-screen would result in the dot also being off-screen.
-        bubble.getIconView().setDotPositionOnLeft(
-                !mStackOnLeftOrWillBe /* onLeft */, false /* animate */);
+        bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
 
         bubble.getIconView().setOnClickListener(mBubbleClickListener);
         bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
@@ -1520,7 +1515,7 @@
             Bubble bubble = bubbles.get(i);
             mBubbleContainer.reorderView(bubble.getIconView(), i);
         }
-        updateBubbleZOrdersAndDotPosition(false /* animate */);
+        updateBubbleIcons();
         updatePointerPosition();
     }
 
@@ -2363,7 +2358,7 @@
         // name and icon.
         if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
             final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
-            mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon());
+            mManageSettingsIcon.setImageDrawable(bubble.getAppBadge());
             mManageSettingsText.setText(getResources().getString(
                     R.string.bubbles_app_settings, bubble.getAppName()));
         }
@@ -2551,28 +2546,31 @@
         }
 
         mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
-        updateBubbleZOrdersAndDotPosition(false);
+        updateBubbleIcons();
     }
 
-    /** Sets the appropriate Z-order and dot position for each bubble in the stack. */
-    private void updateBubbleZOrdersAndDotPosition(boolean animate) {
+    /**
+     * Sets the appropriate Z-order, badge, and dot position for each bubble in the stack.
+     * Animate dot and badge changes.
+     */
+    private void updateBubbleIcons() {
         int bubbleCount = getBubbleCount();
         for (int i = 0; i < bubbleCount; i++) {
             BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
             bv.setZ((mMaxBubbles * mBubbleElevation) - i);
 
-            // If the dot is on the left, and so is the stack, we need to change the dot position.
-            if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) {
-                bv.setDotPositionOnLeft(!mStackOnLeftOrWillBe, animate);
-            }
-
-            if (!mIsExpanded && i > 0) {
-                // If we're collapsed and this bubble is behind other bubbles, suppress its dot.
-                bv.addDotSuppressionFlag(
-                        BadgedImageView.SuppressionFlag.BEHIND_STACK);
-            } else {
+            if (mIsExpanded) {
                 bv.removeDotSuppressionFlag(
                         BadgedImageView.SuppressionFlag.BEHIND_STACK);
+                bv.animateDotBadgePositions(false /* onLeft */);
+            } else if (i == 0) {
+                bv.removeDotSuppressionFlag(
+                        BadgedImageView.SuppressionFlag.BEHIND_STACK);
+                bv.animateDotBadgePositions(!mStackOnLeftOrWillBe);
+            } else {
+                bv.addDotSuppressionFlag(
+                        BadgedImageView.SuppressionFlag.BEHIND_STACK);
+                bv.hideBadge();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 5749169..28757fa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -120,8 +120,8 @@
         BubbleExpandedView expandedView;
         ShortcutInfo shortcutInfo;
         String appName;
-        Bitmap badgedBubbleImage;
-        Drawable badgedAppIcon;
+        Bitmap bubbleBitmap;
+        Drawable badgeDrawable;
         int dotColor;
         Path dotPath;
         Bubble.FlyoutMessage flyoutMessage;
@@ -179,9 +179,10 @@
 
             BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
                     b.isImportantConversation());
-            info.badgedAppIcon = badgedIcon;
-            info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
-                    badgeBitmapInfo).icon;
+            info.badgeDrawable = badgedIcon;
+            info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable,
+                    null /* user */,
+                    true /* shrinkNonAdaptiveIcons */).icon;
 
             // Dot color & placement
             Path iconPath = PathParser.createPathFromPathData(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index f1a01be..916ad18 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Path;
+import android.graphics.drawable.Drawable;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -34,12 +35,17 @@
 
     String getKey();
 
-    Bitmap getBadgedImage();
+    /** Bubble icon bitmap with no badge and no dot. */
+    Bitmap getBubbleIcon();
+
+    /** App badge drawable to draw above bubble icon. */
+    @Nullable Drawable getAppBadge();
+
+    /** Path of normalized bubble icon to draw dot on. */
+    Path getDotPath();
 
     int getDotColor();
 
-    Path getDotPath();
-
     boolean showDot();
 
     int getDisplayId();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
index 9dcc3bb..a7d17e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
@@ -177,7 +177,7 @@
                     userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
                 }
             })
-        userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
+        userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT, 1f)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index b464e8ad..89b5c38 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -22,8 +22,8 @@
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 
-import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -328,14 +328,14 @@
             return false;
         }
 
-        // Bail early if the pinned stack is staled.
-        final ActivityManager.StackInfo pinnedStackInfo;
+        // Bail early if the pinned task is staled.
+        final RootTaskInfo pinnedTaskInfo;
         try {
-            pinnedStackInfo = ActivityTaskManager.getService()
-                    .getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (pinnedStackInfo == null) return false;
+            pinnedTaskInfo = ActivityTaskManager.getService()
+                    .getRootTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+            if (pinnedTaskInfo == null) return false;
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get StackInfo for pinned stack", e);
+            Log.e(TAG, "Failed to get RootTaskInfo for pinned task", e);
             return false;
         }
 
@@ -362,7 +362,7 @@
 
         getInsetBounds(outInsetBounds);
         outBounds.set(postChangeStackBounds);
-        t.setBounds(pinnedStackInfo.stackToken, outBounds);
+        t.setBounds(pinnedTaskInfo.token, outBounds);
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 5793cdd..a5ee3a0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -99,6 +99,36 @@
     private static final int MSG_FINISH_RESIZE = 4;
     private static final int MSG_RESIZE_USER = 5;
 
+    // Not a complete set of states but serves what we want right now.
+    private enum State {
+        UNDEFINED(0),
+        TASK_APPEARED(1),
+        ENTERING_PIP(2),
+        EXITING_PIP(3);
+
+        private final int mStateValue;
+
+        State(int value) {
+            mStateValue = value;
+        }
+
+        private boolean isInPip() {
+            return mStateValue >= TASK_APPEARED.mStateValue
+                    && mStateValue != EXITING_PIP.mStateValue;
+        }
+
+        /**
+         * Resize request can be initiated in other component, ignore if we are no longer in PIP,
+         * still waiting for animation or we're exiting from it.
+         *
+         * @return {@code true} if the resize request should be blocked/ignored.
+         */
+        private boolean shouldBlockResizeRequest() {
+            return mStateValue < ENTERING_PIP.mStateValue
+                    || mStateValue == EXITING_PIP.mStateValue;
+        }
+    }
+
     private final Context mContext;
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
@@ -200,8 +230,7 @@
     private ActivityManager.RunningTaskInfo mTaskInfo;
     private WindowContainerToken mToken;
     private SurfaceControl mLeash;
-    private boolean mInPip;
-    private boolean mExitingPip;
+    private State mState = State.UNDEFINED;
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
@@ -253,11 +282,11 @@
     }
 
     public boolean isInPip() {
-        return mInPip;
+        return mState.isInPip();
     }
 
     public boolean isDeferringEnterPipAnimation() {
-        return mInPip && mShouldDeferEnteringPip;
+        return mState.isInPip() && mShouldDeferEnteringPip;
     }
 
     /**
@@ -286,9 +315,9 @@
      * @param animationDurationMs duration in millisecond for the exiting PiP transition
      */
     public void exitPip(int animationDurationMs) {
-        if (!mInPip || mExitingPip || mToken == null) {
+        if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) {
             Log.wtf(TAG, "Not allowed to exitPip in current state"
-                    + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
+                    + " mState=" + mState + " mToken=" + mToken);
             return;
         }
 
@@ -303,6 +332,7 @@
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                 : TRANSITION_DIRECTION_LEAVE_PIP;
         if (orientationDiffers) {
+            mState = State.EXITING_PIP;
             // Send started callback though animation is ignored.
             sendOnPipTransitionStarted(direction);
             // Don't bother doing an animation if the display rotation differs or if it's in
@@ -311,7 +341,6 @@
             mTaskOrganizer.applyTransaction(wct);
             // Send finished callback though animation is ignored.
             sendOnPipTransitionFinished(direction);
-            mInPip = false;
         } else {
             final SurfaceControl.Transaction tx =
                     mSurfaceControlTransactionFactory.getTransaction();
@@ -333,11 +362,10 @@
                     scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
                             null /* sourceHintRect */, direction, animationDurationMs,
                             null /* updateBoundsCallback */);
-                    mInPip = false;
+                    mState = State.EXITING_PIP;
                 }
             });
         }
-        mExitingPip = true;
     }
 
     private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
@@ -356,9 +384,9 @@
      * Removes PiP immediately.
      */
     public void removePip() {
-        if (!mInPip || mExitingPip ||  mToken == null) {
+        if (!mState.isInPip() ||  mToken == null) {
             Log.wtf(TAG, "Not allowed to removePip in current state"
-                    + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
+                    + " mState=" + mState + " mToken=" + mToken);
             return;
         }
 
@@ -370,7 +398,7 @@
                 .setDuration(mEnterExitAnimationDuration)
                 .start());
         mInitialState.remove(mToken.asBinder());
-        mExitingPip = true;
+        mState = State.EXITING_PIP;
     }
 
     private void removePipImmediately() {
@@ -392,8 +420,7 @@
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
         mTaskInfo = info;
         mToken = mTaskInfo.token;
-        mInPip = true;
-        mExitingPip = false;
+        mState = State.TASK_APPEARED;
         mLeash = leash;
         mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
         mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
@@ -423,6 +450,7 @@
             scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
                     TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
                     null /* updateBoundsCallback */);
+            mState = State.ENTERING_PIP;
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             enterPipWithAlphaAnimation(destinationBounds, mEnterExitAnimationDuration);
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -468,6 +496,9 @@
                         .setPipAnimationCallback(mPipAnimationCallback)
                         .setDuration(durationMs)
                         .start());
+                // mState is set right after the animation is kicked off to block any resize
+                // requests such as offsetPip that may have been called prior to the transition.
+                mState = State.ENTERING_PIP;
             }
         });
     }
@@ -560,7 +591,7 @@
      */
     @Override
     public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
-        if (!mInPip) {
+        if (!mState.isInPip()) {
             return;
         }
         final WindowContainerToken token = info.token;
@@ -571,8 +602,7 @@
         }
         mShouldDeferEnteringPip = false;
         mPictureInPictureParams = null;
-        mInPip = false;
-        mExitingPip = false;
+        mState = State.UNDEFINED;
         mPipUiEventLoggerLogger.setTaskInfo(null);
     }
 
@@ -600,7 +630,7 @@
 
     @Override
     public void onFixedRotationFinished(int displayId) {
-        if (mShouldDeferEnteringPip && mInPip) {
+        if (mShouldDeferEnteringPip && mState.isInPip()) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
                     mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
                     null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
@@ -623,7 +653,7 @@
                 mPipAnimationController.getCurrentAnimator();
         if (animator == null || !animator.isRunning()
                 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
-            if (mInPip && fromRotation) {
+            if (mState.isInPip() && fromRotation) {
                 // If we are rotating while there is a current animation, immediately cancel the
                 // animation (remove the listeners so we don't trigger the normal finish resize
                 // call that should only happen on the update thread)
@@ -712,10 +742,10 @@
     private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
             Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
             int durationMs, Consumer<Rect> updateBoundsCallback) {
-        if (!mInPip) {
+        if (!mState.isInPip()) {
             // TODO: tend to use shouldBlockResizeRequest here as well but need to consider
             // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
-            // container transaction callback and we want to set the mExitingPip immediately.
+            // container transaction callback and we want to set the mState immediately.
             return;
         }
 
@@ -772,7 +802,7 @@
     private void scheduleFinishResizePip(Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
             Consumer<Rect> updateBoundsCallback) {
-        if (shouldBlockResizeRequest()) {
+        if (mState.shouldBlockResizeRequest()) {
             return;
         }
 
@@ -791,7 +821,7 @@
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
                 .resetScale(tx, mLeash, destinationBounds)
-                .round(tx, mLeash, mInPip);
+                .round(tx, mLeash, mState.isInPip());
         return tx;
     }
 
@@ -800,7 +830,7 @@
      */
     public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
             Consumer<Rect> updateBoundsCallback) {
-        if (shouldBlockResizeRequest()) {
+        if (mState.shouldBlockResizeRequest()) {
             return;
         }
         if (mShouldDeferEnteringPip) {
@@ -845,7 +875,7 @@
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
-                .round(tx, mLeash, mInPip);
+                .round(tx, mLeash, mState.isInPip());
         tx.apply();
     }
 
@@ -983,16 +1013,6 @@
     }
 
     /**
-     * Resize request can be initiated in other component, ignore if we are no longer in PIP
-     * or we're exiting from it.
-     *
-     * @return {@code true} if the resize request should be blocked/ignored.
-     */
-    private boolean shouldBlockResizeRequest() {
-        return !mInPip || mExitingPip;
-    }
-
-    /**
      * Sync with {@link SplitScreen} on destination bounds if PiP is going to split screen.
      *
      * @param destinationBoundsOut contain the updated destination bounds if applicable
@@ -1026,7 +1046,7 @@
         pw.println(innerPrefix + "mToken=" + mToken
                 + " binder=" + (mToken != null ? mToken.asBinder() : null));
         pw.println(innerPrefix + "mLeash=" + mLeash);
-        pw.println(innerPrefix + "mInPip=" + mInPip);
+        pw.println(innerPrefix + "mState=" + mState);
         pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
         pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
         pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
index 6998e90..4f225e2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
@@ -25,6 +25,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityManager;
 import android.app.RemoteAction;
 import android.content.ComponentName;
@@ -324,9 +325,9 @@
         configController.addCallback(mOverlayChangedListener);
 
         try {
-            ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo(
+            RootTaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (stackInfo != null) {
+            if (taskInfo != null) {
                 // If SystemUI restart, and it already existed a pinned stack,
                 // register the pip input consumer to ensure touch can send to it.
                 mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 873ba26..d308172 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -19,8 +19,8 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -312,10 +312,10 @@
             // Fetch the pinned stack bounds
             Rect stackBounds = null;
             try {
-                StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
+                RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
                         WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-                if (pinnedStackInfo != null) {
-                    stackBounds = pinnedStackInfo.bounds;
+                if (pinnedTaskInfo != null) {
+                    stackBounds = pinnedTaskInfo.bounds;
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, "Error showing PIP menu", e);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
index 993bfe0..c66f442 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -475,12 +475,10 @@
         final Pair<ComponentName, Integer> topPipActivityInfo =
                 PipUtils.getTopPipActivity(mContext, ActivityManager.getService());
         if (topPipActivityInfo.first != null) {
-            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
             final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
                     Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
-            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
             settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
-            mContext.startActivity(settingsIntent);
+            mContext.startActivityAsUser(settingsIntent, UserHandle.CURRENT);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 50d20d3..08d9b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.pip.phone;
 
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_PINCH_RESIZE;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
@@ -46,6 +46,7 @@
 import android.view.InputEventReceiver;
 import android.view.InputMonitor;
 import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
 import android.view.ViewConfiguration;
 
 import com.android.internal.policy.TaskResizingAlgorithm;
@@ -67,6 +68,8 @@
 public class PipResizeGestureHandler {
 
     private static final String TAG = "PipResizeGestureHandler";
+    private static final float PINCH_THRESHOLD = 0.05f;
+    private static final float STARTING_SCALE_FACTOR = 1.0f;
 
     private static final int INVALID_SYSUI_STATE_MASK =
             SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
@@ -83,6 +86,7 @@
     private final int mDisplayId;
     private final Executor mMainExecutor;
     private final SysUiState mSysUiState;
+    private final ScaleGestureDetector mScaleGestureDetector;
     private final Region mTmpRegion = new Region();
 
     private final PointF mDownPoint = new PointF();
@@ -105,8 +109,10 @@
     private boolean mAllowGesture;
     private boolean mIsAttached;
     private boolean mIsEnabled;
-    private boolean mEnableUserResize;
+    private boolean mEnablePinchResize;
     private boolean mThresholdCrossed;
+    private boolean mUsingPinchToZoom = false;
+    private float mScaleFactor = STARTING_SCALE_FACTOR;
 
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
@@ -136,17 +142,73 @@
         context.getDisplay().getRealSize(mMaxSize);
         reloadResources();
 
-        mEnableUserResize = DeviceConfig.getBoolean(
+        mScaleGestureDetector = new ScaleGestureDetector(context,
+                new ScaleGestureDetector.OnScaleGestureListener() {
+                    @Override
+                    public boolean onScale(ScaleGestureDetector detector) {
+                        mScaleFactor *= detector.getScaleFactor();
+
+                        if (!mThresholdCrossed
+                                && (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD)
+                                || mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) {
+                            mThresholdCrossed = true;
+                            mInputMonitor.pilferPointers();
+                        }
+                        if (mThresholdCrossed) {
+                            int height = Math.min(mMaxSize.y, Math.max(mMinSize.y,
+                                    (int) (mScaleFactor * mLastDownBounds.height())));
+                            int width = Math.min(mMaxSize.x, Math.max(mMinSize.x,
+                                    (int) (mScaleFactor * mLastDownBounds.width())));
+                            int top, bottom, left, right;
+
+                            if ((mCtrlType & CTRL_TOP) != 0) {
+                                top = mLastDownBounds.bottom - height;
+                                bottom = mLastDownBounds.bottom;
+                            } else {
+                                top = mLastDownBounds.top;
+                                bottom = mLastDownBounds.top + height;
+                            }
+
+                            if ((mCtrlType & CTRL_LEFT) != 0) {
+                                left = mLastDownBounds.right - width;
+                                right = mLastDownBounds.right;
+                            } else {
+                                left = mLastDownBounds.left;
+                                right = mLastDownBounds.left + width;
+                            }
+
+                            mLastResizeBounds.set(left, top, right, bottom);
+                            mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds,
+                                    mLastResizeBounds,
+                                    null);
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public boolean onScaleBegin(ScaleGestureDetector detector) {
+                        setCtrlTypeForPinchToZoom();
+                        return true;
+                    }
+
+                    @Override
+                    public void onScaleEnd(ScaleGestureDetector detector) {
+                        mScaleFactor = STARTING_SCALE_FACTOR;
+                        finishResize();
+                    }
+                });
+
+        mEnablePinchResize = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
-                PIP_USER_RESIZE,
-                /* defaultValue = */ true);
+                PIP_PINCH_RESIZE,
+                /* defaultValue = */ false);
         deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
                 new DeviceConfig.OnPropertiesChangedListener() {
                     @Override
                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                        if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
-                            mEnableUserResize = properties.getBoolean(
-                                    PIP_USER_RESIZE, /* defaultValue = */ true);
+                        if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) {
+                            mEnablePinchResize = properties.getBoolean(
+                                    PIP_PINCH_RESIZE, /* defaultValue = */ false);
                         }
                     }
                 });
@@ -193,7 +255,7 @@
     }
 
     private void updateIsEnabled() {
-        boolean isEnabled = mIsAttached && mEnableUserResize;
+        boolean isEnabled = mIsAttached;
         if (isEnabled == mIsEnabled) {
             return;
         }
@@ -211,7 +273,11 @@
 
     private void onInputEvent(InputEvent ev) {
         if (ev instanceof MotionEvent) {
-            onMotionEvent((MotionEvent) ev);
+            if (mUsingPinchToZoom) {
+                mScaleGestureDetector.onTouchEvent((MotionEvent) ev);
+            } else {
+                onDragCornerResize((MotionEvent) ev);
+            }
         }
     }
 
@@ -254,8 +320,51 @@
     }
 
     public boolean willStartResizeGesture(MotionEvent ev) {
-        return mEnableUserResize && isInValidSysUiState()
-                && isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY());
+        if (isInValidSysUiState()) {
+            switch (ev.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    // Always pass the DOWN event to the ScaleGestureDetector
+                    mScaleGestureDetector.onTouchEvent(ev);
+                    if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+                        return true;
+                    }
+                    break;
+
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    if (mEnablePinchResize && ev.getPointerCount() == 2) {
+                        mUsingPinchToZoom = true;
+                        return true;
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+        return false;
+    }
+
+    private void setCtrlTypeForPinchToZoom() {
+        final Rect currentPipBounds = mMotionHelper.getBounds();
+        mLastDownBounds.set(mMotionHelper.getBounds());
+
+        Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
+        mDisplayBounds.set(movementBounds.left,
+                movementBounds.top,
+                movementBounds.right + currentPipBounds.width(),
+                movementBounds.bottom + currentPipBounds.height());
+
+        if (currentPipBounds.left == mDisplayBounds.left) {
+            mCtrlType |= CTRL_RIGHT;
+        } else {
+            mCtrlType |= CTRL_LEFT;
+        }
+
+        if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) {
+            mCtrlType |= CTRL_TOP;
+        } else {
+            mCtrlType |= CTRL_BOTTOM;
+        }
     }
 
     private void setCtrlType(int x, int y) {
@@ -295,7 +404,7 @@
         return (mSysUiState.getFlags() & INVALID_SYSUI_STATE_MASK) == 0;
     }
 
-    private void onMotionEvent(MotionEvent ev) {
+    private void onDragCornerResize(MotionEvent ev) {
         int action = ev.getActionMasked();
         float x = ev.getX();
         float y = ev.getY();
@@ -345,28 +454,33 @@
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    if (!mLastResizeBounds.isEmpty()) {
-                        mUserResizeBounds.set(mLastResizeBounds);
-                        mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
-                                (Rect bounds) -> {
-                                    new Handler(Looper.getMainLooper()).post(() -> {
-                                        mMotionHelper.synchronizePinnedStackBounds();
-                                        mUpdateMovementBoundsRunnable.run();
-                                        resetState();
-                                    });
-                                });
-                        mPipUiEventLogger.log(
-                                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
-                    } else {
-                        resetState();
-                    }
+                    finishResize();
                     break;
             }
         }
     }
 
+    private void finishResize() {
+        if (!mLastResizeBounds.isEmpty()) {
+            mUserResizeBounds.set(mLastResizeBounds);
+            mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
+                    (Rect bounds) -> {
+                        new Handler(Looper.getMainLooper()).post(() -> {
+                            mMotionHelper.synchronizePinnedStackBounds();
+                            mUpdateMovementBoundsRunnable.run();
+                            resetState();
+                        });
+                    });
+            mPipUiEventLogger.log(
+                    PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
+        } else {
+            resetState();
+        }
+    }
+
     private void resetState() {
         mCtrlType = CTRL_NONE;
+        mUsingPinchToZoom = false;
         mAllowGesture = false;
         mThresholdCrossed = false;
     }
@@ -397,7 +511,7 @@
         pw.println(innerPrefix + "mAllowGesture=" + mAllowGesture);
         pw.println(innerPrefix + "mIsAttached=" + mIsAttached);
         pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled);
-        pw.println(innerPrefix + "mEnableUserResize=" + mEnableUserResize);
+        pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize);
         pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 9693f23..97b3484 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -650,8 +650,7 @@
         }
 
         MotionEvent ev = (MotionEvent) inputEvent;
-        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
-                && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
+        if (mPipResizeGestureHandler.willStartResizeGesture(ev)) {
             // Initialize the touch state for the gesture, but immediately reset to invalidate the
             // gesture
             mTouchState.onTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index 4cfec01..baa8f11 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -19,8 +19,8 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -40,15 +40,15 @@
             IActivityManager activityManager) {
         try {
             final String sysUiPackageName = context.getPackageName();
-            final StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
+            final RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
-                    pinnedStackInfo.taskIds.length > 0) {
-                for (int i = pinnedStackInfo.taskNames.length - 1; i >= 0; i--) {
+            if (pinnedTaskInfo != null && pinnedTaskInfo.childTaskIds != null
+                    && pinnedTaskInfo.childTaskIds.length > 0) {
+                for (int i = pinnedTaskInfo.childTaskNames.length - 1; i >= 0; i--) {
                     ComponentName cn = ComponentName.unflattenFromString(
-                            pinnedStackInfo.taskNames[i]);
+                            pinnedTaskInfo.childTaskNames[i]);
                     if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
-                        return new Pair<>(cn, pinnedStackInfo.taskUserIds[i]);
+                        return new Pair<>(cn, pinnedTaskInfo.childTaskUserIds[i]);
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
index 3b3235f..12a545a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
@@ -21,8 +21,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
 import android.app.RemoteAction;
 import android.content.BroadcastReceiver;
@@ -289,7 +289,7 @@
         //   1. Configuration changed due to the language change (RTL <-> RTL)
         //   2. SystemUI restarts after the crash
         mPipBounds = mDefaultPipBounds;
-        resizePinnedStack(getPinnedStackInfo() == null ? STATE_NO_PIP : STATE_PIP);
+        resizePinnedStack(getPinnedTaskInfo() == null ? STATE_NO_PIP : STATE_PIP);
     }
 
     /**
@@ -505,15 +505,15 @@
         return mState != STATE_NO_PIP;
     }
 
-    private StackInfo getPinnedStackInfo() {
-        StackInfo stackInfo = null;
+    private RootTaskInfo getPinnedTaskInfo() {
+        RootTaskInfo taskInfo = null;
         try {
-            stackInfo = ActivityTaskManager.getService().getStackInfo(
+            taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
         } catch (RemoteException e) {
-            Log.e(TAG, "getStackInfo failed", e);
+            Log.e(TAG, "getRootTaskInfo failed", e);
         }
-        return stackInfo;
+        return taskInfo;
     }
 
     private void handleMediaResourceGranted(String[] packageNames) {
@@ -611,14 +611,14 @@
             if (getState() != STATE_NO_PIP) {
                 boolean hasPip = false;
 
-                StackInfo stackInfo = getPinnedStackInfo();
-                if (stackInfo == null || stackInfo.taskIds == null) {
+                RootTaskInfo taskInfo = getPinnedTaskInfo();
+                if (taskInfo == null || taskInfo.childTaskIds == null) {
                     Log.w(TAG, "There is nothing in pinned stack");
                     closePipInternal(false);
                     return;
                 }
-                for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
-                    if (stackInfo.taskIds[i] == mPipTaskId) {
+                for (int i = taskInfo.childTaskIds.length - 1; i >= 0; --i) {
+                    if (taskInfo.childTaskIds[i] == mPipTaskId) {
                         // PIP task is still alive.
                         hasPip = true;
                         break;
@@ -642,16 +642,16 @@
         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             if (DEBUG) Log.d(TAG, "onActivityPinned()");
 
-            StackInfo stackInfo = getPinnedStackInfo();
-            if (stackInfo == null) {
+            RootTaskInfo taskInfo = getPinnedTaskInfo();
+            if (taskInfo == null) {
                 Log.w(TAG, "Cannot find pinned stack");
                 return;
             }
-            if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
-            mPinnedStackId = stackInfo.stackId;
-            mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
+            if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
+            mPinnedStackId = taskInfo.taskId;
+            mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
             mPipComponentName = ComponentName.unflattenFromString(
-                    stackInfo.taskNames[stackInfo.taskNames.length - 1]);
+                    taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]);
             // Set state to STATE_PIP so we show it when the pinned stack animation ends.
             mState = STATE_PIP;
             mMediaSessionManager.addOnActiveSessionsChangedListener(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 4d6d71c..0fbd73b 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -86,9 +86,9 @@
                 ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
     }
 
+    // TODO(b/168209929) Remove hardcode
     private fun isMicCameraEnabled(): Boolean {
-        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                MIC_CAMERA, DEFAULT_MIC_CAMERA)
+        return true
     }
 
     private var currentUserIds = emptyList<Int>()
@@ -124,11 +124,11 @@
                             DEFAULT_ALL_INDICATORS)
                     callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                 }
-
-                if (properties.keyset.contains(MIC_CAMERA)) {
-                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
-                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
-                }
+                // TODO(b/168209929) Uncomment
+//                if (properties.keyset.contains(MIC_CAMERA)) {
+//                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
+//                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
+//                }
                 internalUiExecutor.updateListeningState()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cba938f..2a976f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -75,8 +75,6 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.onehanded.OneHanded;
-import com.android.systemui.onehanded.OneHandedEvents;
 import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -93,6 +91,8 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEvents;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7dd4edd..2b4fa2a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -574,7 +574,8 @@
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, boolean showFlash) {
         if (mScreenshotLayout.isAttachedToWindow()) {
-            if (!mDismissAnimation.isRunning()) { // if we didn't already dismiss for another reason
+            // if we didn't already dismiss for another reason
+            if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
                 mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
             }
             dismissScreenshot("new screenshot requested", true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9586795..a816ecc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -516,6 +516,7 @@
         return mLifetimeExtenders;
     }
 
+    @Nullable
     public RemoteInputController getController() {
         return mRemoteInputController;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 6f7b32b..363a085 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.AppGlobals;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -161,16 +162,16 @@
                 () -> {
                     ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs);
                     try {
-                        final ActivityManager.StackInfo focusedStack =
-                                ActivityTaskManager.getService().getFocusedStackInfo();
-                        if (focusedStack != null) {
+                        final RootTaskInfo focusedTask =
+                                ActivityTaskManager.getService().getFocusedRootTaskInfo();
+                        if (focusedTask != null) {
                             final int windowingMode =
-                                    focusedStack.configuration.windowConfiguration
+                                    focusedTask.configuration.windowConfiguration
                                             .getWindowingMode();
                             if (windowingMode == WINDOWING_MODE_FULLSCREEN
                                     || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                                     || windowingMode == WINDOWING_MODE_FREEFORM) {
-                                checkAndPostForStack(focusedStack, notifs, noMan, pm);
+                                checkAndPostForStack(focusedTask, notifs, noMan, pm);
                             }
                         }
                         if (mDockedStackExists) {
@@ -205,10 +206,8 @@
             @NonNull NotificationManager noMan,
             @NonNull IPackageManager pm) {
         try {
-            final ActivityManager.StackInfo info =
-                    ActivityTaskManager.getService()
-                            .getStackInfo(
-                                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+            final RootTaskInfo info = ActivityTaskManager.getService().getRootTaskInfo(
+                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
             checkAndPostForStack(info, notifs, noMan, pm);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
@@ -221,7 +220,7 @@
      * exists, this method removes it from {@code notifs} in the arguments.
      */
     private void checkAndPostForStack(
-            @Nullable ActivityManager.StackInfo info,
+            @Nullable RootTaskInfo info,
             @NonNull ArraySet<Pair<String, Integer>> notifs,
             @NonNull NotificationManager noMan,
             @NonNull IPackageManager pm) {
@@ -241,7 +240,7 @@
                             info.userId,
                             appInfo,
                             noMan,
-                            info.taskIds[info.taskIds.length - 1]);
+                            info.childTaskIds[info.childTaskIds.length - 1]);
                 }
             }
         } catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 82c1f24..fd0476b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -110,4 +110,11 @@
         mPreviousAttachState.clone(mAttachState);
         mAttachState.reset();
     }
+
+    /**
+     * True if this entry was attached in the last pass, else false.
+     */
+    public boolean wasAttachedInPreviousPass() {
+        return getPreviousAttachState().getParent() != null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 2b545c5..a0ef1b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -513,28 +513,66 @@
         }
     }
 
-    private void stabilizeGroupingNotifs(List<ListEntry> list) {
+    private void stabilizeGroupingNotifs(List<ListEntry> topLevelList) {
         if (mNotifStabilityManager == null) {
             return;
         }
 
-        for (int i = 0; i < list.size(); i++) {
-            final ListEntry tle = list.get(i);
-            if (tle.getPreviousAttachState().getParent() == null) {
-                continue; // new entries are allowed
-            }
-
-            final GroupEntry prevParent = tle.getPreviousAttachState().getParent();
-            final GroupEntry assignedParent = tle.getParent();
-            if (prevParent != assignedParent) {
-                if (!mNotifStabilityManager.isGroupChangeAllowed(tle.getRepresentativeEntry())) {
-                    tle.getAttachState().getSuppressedChanges().setParent(assignedParent);
-                    tle.setParent(prevParent);
+        for (int i = 0; i < topLevelList.size(); i++) {
+            final ListEntry tle = topLevelList.get(i);
+            if (tle instanceof GroupEntry) {
+                // maybe put children back into their old group (including moving back to top-level)
+                GroupEntry groupEntry = (GroupEntry) tle;
+                List<NotificationEntry> children = groupEntry.getRawChildren();
+                for (int j = 0; j < groupEntry.getChildren().size(); j++) {
+                    if (maybeSuppressGroupChange(children.get(j), topLevelList)) {
+                        // child was put back into its previous group, so we remove it from this
+                        // group
+                        children.remove(j);
+                        j--;
+                    }
+                }
+            } else {
+                // maybe put top-level-entries back into their previous groups
+                if (maybeSuppressGroupChange(tle.getRepresentativeEntry(), topLevelList)) {
+                    // entry was put back into its previous group, so we remove it from the list of
+                    // top-level-entries
+                    topLevelList.remove(i);
+                    i--;
                 }
             }
         }
     }
 
+    /**
+     * Returns true if the group change was suppressed, else false
+     */
+    private boolean maybeSuppressGroupChange(NotificationEntry entry, List<ListEntry> out) {
+        if (!entry.wasAttachedInPreviousPass()) {
+            return false; // new entries are allowed
+        }
+
+        final GroupEntry prevParent = entry.getPreviousAttachState().getParent();
+        final GroupEntry assignedParent = entry.getParent();
+        if (prevParent != assignedParent
+                && !mNotifStabilityManager.isGroupChangeAllowed(entry.getRepresentativeEntry())) {
+            entry.getAttachState().getSuppressedChanges().setParent(assignedParent);
+            entry.setParent(prevParent);
+            if (prevParent == ROOT_ENTRY) {
+                out.add(entry);
+            } else if (prevParent != null) {
+                prevParent.addChild(entry);
+                if (!mGroups.containsKey(prevParent.getKey())) {
+                    mGroups.put(prevParent.getKey(), prevParent);
+                }
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
     private void promoteNotifs(List<ListEntry> list) {
         for (int i = 0; i < list.size(); i++) {
             final ListEntry tle = list.get(i);
@@ -577,6 +615,17 @@
 
                 } else if (group.getSummary() == null
                         || children.size() < MIN_CHILDREN_FOR_GROUP) {
+
+                    if (group.getSummary() != null
+                            && group.wasAttachedInPreviousPass()
+                            && mNotifStabilityManager != null
+                            && !mNotifStabilityManager.isGroupChangeAllowed(group.getSummary())) {
+                        // if this group was previously attached and group changes aren't
+                        // allowed, keep it around until group changes are allowed again
+                        group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
+                        continue;
+                    }
+
                     // If the group doesn't provide a summary or is too small, ignore it and add
                     // its children (if any) directly to top-level.
 
@@ -673,7 +722,6 @@
                 GroupEntry parent = (GroupEntry) entry;
                 for (NotificationEntry child : parent.getChildren()) {
                     child.getAttachState().setSection(section);
-                    child.getAttachState().setSection(section);
                 }
                 parent.sortChildren(sChildComparator);
             }
@@ -719,6 +767,12 @@
                         curr.getParent());
             }
 
+            if (curr.getSuppressedChanges().getWasPruneSuppressed()) {
+                mLogger.logGroupPruningSuppressed(
+                        mIterationCount,
+                        curr.getParent());
+            }
+
             if (curr.getExcludingFilter() != prev.getExcludingFilter()) {
                 mLogger.logFilterChanged(
                         mIterationCount,
@@ -868,9 +922,9 @@
 
         NotifSection finalSection = newSection;
 
-        // are we changing sections of this entry?
+        // have we seen this entry before and are we changing its section?
         if (mNotifStabilityManager != null
-                && prevAttachState.getParent() != null
+                && entry.wasAttachedInPreviousPass()
                 && newSection != prevAttachState.getSection()) {
 
             // are section changes allowed?
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
index 3eb2e61..584563b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
@@ -24,28 +24,37 @@
  */
 data class SuppressedAttachState private constructor(
     /**
-     * Null if not attached to the current shade list. If top-level, then the shade list root. If
-     * part of a group, then that group's GroupEntry.
+     * The suppressed section assignment for this ListEntry.
+     * Null if no section change was suppressed.
+     */
+    var section: NotifSection?,
+
+    /**
+     * The suppressed parent assignment for this ListEntry.
+     *  - Null if no parent change was suppressed.
+     *  - Root if suppressing group change to top-level
+     *  - GroupEntry if suppressing group change to a different group
      */
     var parent: GroupEntry?,
 
     /**
-     * The assigned section for this ListEntry. If the child of the group, this will be the
-     * parent's section. Null if not attached to the list.
+     * Whether the ListEntry would have been pruned had its group change not been suppressed.
      */
-    var section: NotifSection?
+    var wasPruneSuppressed: Boolean
 ) {
 
     /** Copies the state of another instance. */
     fun clone(other: SuppressedAttachState) {
         parent = other.parent
         section = other.section
+        wasPruneSuppressed = other.wasPruneSuppressed
     }
 
     /** Resets back to a "clean" state (the same as created by the factory method) */
     fun reset() {
         parent = null
         section = null
+        wasPruneSuppressed = false
     }
 
     companion object {
@@ -53,7 +62,8 @@
         fun create(): SuppressedAttachState {
             return SuppressedAttachState(
                 null,
-                null)
+                null,
+                false)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 3aaa9ac..f0eb084 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.dagger.PeopleHeader
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import javax.inject.Inject
@@ -33,7 +35,8 @@
  */
 @SysUISingleton
 class ConversationCoordinator @Inject constructor(
-    private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+    private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+    @PeopleHeader peopleHeaderController: NodeController
 ) : Coordinator {
 
     private val notificationPromoter = object : NotifPromoter(TAG) {
@@ -43,9 +46,9 @@
     }
 
     val sectioner = object : NotifSectioner("People") {
-        override fun isInSection(entry: ListEntry): Boolean {
-            return isConversation(entry.representativeEntry!!)
-        }
+        override fun isInSection(entry: ListEntry): Boolean =
+                isConversation(entry.representativeEntry!!)
+        override fun getHeaderNodeController() = peopleHeaderController
     }
 
     override fun attach(pipeline: NotifPipeline) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 24e912e..be1383f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -30,6 +30,8 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader;
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -61,6 +63,7 @@
     private final HeadsUpViewBinder mHeadsUpViewBinder;
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final NotificationRemoteInputManager mRemoteInputManager;
+    private final NodeController mIncomingHeaderController;
 
     // tracks the current HeadUpNotification reported by HeadsUpManager
     private @Nullable NotificationEntry mCurrentHun;
@@ -73,11 +76,13 @@
             HeadsUpManager headsUpManager,
             HeadsUpViewBinder headsUpViewBinder,
             NotificationInterruptStateProvider notificationInterruptStateProvider,
-            NotificationRemoteInputManager remoteInputManager) {
+            NotificationRemoteInputManager remoteInputManager,
+            @IncomingHeader NodeController incomingHeaderController) {
         mHeadsUpManager = headsUpManager;
         mHeadsUpViewBinder = headsUpViewBinder;
         mNotificationInterruptStateProvider = notificationInterruptStateProvider;
         mRemoteInputManager = remoteInputManager;
+        mIncomingHeaderController = incomingHeaderController;
     }
 
     @Override
@@ -196,6 +201,12 @@
         public boolean isInSection(ListEntry entry) {
             return isCurrentlyShowingHun(entry);
         }
+
+        @Nullable
+        @Override
+        public NodeController getHeaderNodeController() {
+            return mIncomingHeaderController;
+        }
     };
 
     private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 0f08e0f..133ddfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
+import android.annotation.Nullable;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -24,6 +26,9 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
+import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 
 import javax.inject.Inject;
 
@@ -40,13 +45,19 @@
 
     private final StatusBarStateController mStatusBarStateController;
     private final HighPriorityProvider mHighPriorityProvider;
+    private final NodeController mSilentHeaderController;
+    private final NodeController mAlertingHeaderController;
 
     @Inject
     public RankingCoordinator(
             StatusBarStateController statusBarStateController,
-            HighPriorityProvider highPriorityProvider) {
+            HighPriorityProvider highPriorityProvider,
+            @AlertingHeader NodeController alertingHeaderController,
+            @SilentHeader NodeController silentHeaderController) {
         mStatusBarStateController = statusBarStateController;
         mHighPriorityProvider = highPriorityProvider;
+        mAlertingHeaderController = alertingHeaderController;
+        mSilentHeaderController = silentHeaderController;
     }
 
     @Override
@@ -70,6 +81,12 @@
         public boolean isInSection(ListEntry entry) {
             return mHighPriorityProvider.isHighPriority(entry);
         }
+
+        @Nullable
+        @Override
+        public NodeController getHeaderNodeController() {
+            return mAlertingHeaderController;
+        }
     };
 
     private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent") {
@@ -77,6 +94,12 @@
         public boolean isInSection(ListEntry entry) {
             return !mHighPriorityProvider.isHighPriority(entry);
         }
+
+        @Nullable
+        @Override
+        public NodeController getHeaderNodeController() {
+            return mSilentHeaderController;
+        }
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 08030f8..5d6c043 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -110,7 +110,7 @@
                 public boolean isGroupChangeAllowed(NotificationEntry entry) {
                     final boolean isGroupChangeAllowedForEntry =
                             mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey());
-                    mIsSuppressingGroupChange |= isGroupChangeAllowedForEntry;
+                    mIsSuppressingGroupChange |= !isGroupChangeAllowedForEntry;
                     return isGroupChangeAllowedForEntry;
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index c09122e..c9fc992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.listbuilder
 
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
 
 data class NotifSection(
     val sectioner: NotifSectioner,
@@ -24,4 +25,7 @@
 ) {
     val label: String
         get() = "Section($index, \"${sectioner.name}\")"
+
+    val headerController: NodeController?
+        get() = sectioner.headerNodeController
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 9ee7db7..5a35127 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -174,8 +174,19 @@
             str1 = suppressedParent?.key
             str2 = keepingParent?.key
         }, {
-            "(Build $long1)     Change of parent to '$str1' suppressed; " +
-                "keeping parent '$str2'"
+            "(Build $long1)     Change of parent to '$str1' suppressed; keeping parent '$str2'"
+        })
+    }
+
+    fun logGroupPruningSuppressed(
+        buildId: Int,
+        keepingParent: GroupEntry?
+    ) {
+        buffer.log(TAG, INFO, {
+            int1 = buildId
+            str1 = keepingParent?.key
+        }, {
+            "(Build $long1)     Group pruning suppressed; keeping parent '$str1'"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
index b57f504..c8982d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
@@ -16,8 +16,12 @@
 
 package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
 
+import android.annotation.Nullable;
+
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.collection.render.NodeSpec;
 
 /**
  * Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}.
@@ -34,4 +38,12 @@
      * notification. The first section to return true determines the section of the notification.
      */
     public abstract boolean isInSection(ListEntry entry);
+
+    /**
+     * Returns an optional {@link NodeSpec} for the section header. If {@code null}, no header will
+     * be used for the section.
+     */
+    public @Nullable NodeController getHeaderNodeController() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index c1f468a..e784ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -56,7 +56,7 @@
             return false;
         }
 
-        return entry.getParent().getChildren().size() == 1;
+        return !isGroupSummary(entry) && entry.getParent().getChildren().size() == 1;
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
index 67f7b1c..727ce20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
@@ -39,9 +39,7 @@
         throw RuntimeException("Not supported")
     }
 
-    fun getChildCount(): Int {
-        throw RuntimeException("Not supported")
-    }
+    fun getChildCount(): Int = 0
 
     fun addChildAt(child: NodeController, index: Int) {
         throw RuntimeException("Not supported")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
new file mode 100644
index 0000000..498b8e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.systemui.statusbar.notification.collection.render
+
+import android.annotation.StringRes
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.dagger.HeaderClick
+import com.android.systemui.statusbar.notification.dagger.HeaderText
+import com.android.systemui.statusbar.notification.dagger.NodeLabel
+import com.android.systemui.statusbar.notification.dagger.SectionHeaderScope
+import com.android.systemui.statusbar.notification.stack.SectionHeaderView
+import javax.inject.Inject
+
+interface SectionHeaderController {
+    fun reinflateView(parent: ViewGroup)
+    val headerView: SectionHeaderView?
+    fun setOnClearAllClickListener(listener: View.OnClickListener)
+}
+
+@SectionHeaderScope
+internal class SectionHeaderNodeControllerImpl @Inject constructor(
+    @NodeLabel override val nodeLabel: String,
+    private val layoutInflater: LayoutInflater,
+    @HeaderText @StringRes private val headerTextResId: Int,
+    @HeaderClick private val onHeaderClickListener: View.OnClickListener
+) : NodeController, SectionHeaderController {
+
+    private var _view: SectionHeaderView? = null
+    private var clearAllClickListener: View.OnClickListener? = null
+
+    override fun reinflateView(parent: ViewGroup) {
+        var oldPos = -1
+        _view?.let { _view ->
+            _view.transientContainer?.removeView(_view)
+            if (_view.parent === parent) {
+                oldPos = parent.indexOfChild(_view)
+                parent.removeView(_view)
+            }
+        }
+        val inflated = layoutInflater.inflate(
+                R.layout.status_bar_notification_section_header,
+                parent,
+                false /* attachToRoot */)
+                as SectionHeaderView
+        inflated.setHeaderText(headerTextResId)
+        inflated.setOnHeaderClickListener(onHeaderClickListener)
+        clearAllClickListener?.let { inflated.setOnClearAllClickListener(it) }
+        if (oldPos != -1) {
+            parent.addView(inflated, oldPos)
+        }
+        _view = inflated
+    }
+
+    override val headerView: SectionHeaderView?
+        get() = _view
+
+    override fun setOnClearAllClickListener(listener: View.OnClickListener) {
+        clearAllClickListener = listener
+        _view?.setOnClearAllClickListener(listener)
+    }
+
+    override val view: View
+        get() = _view!!
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 019520f..22ca496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -65,9 +65,7 @@
      *
      * For debugging purposes.
      */
-    fun getViewLabel(view: View): String {
-        return views[view]?.label ?: view.toString()
-    }
+    fun getViewLabel(view: View): String = views[view]?.label ?: view.toString()
 
     private fun detachChildren(
         parentNode: ShadeNode,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 5243854..c1a63e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -42,46 +42,34 @@
     private val rootController = RootNodeController(listContainer, View(context))
     private val viewDiffer = ShadeViewDiffer(rootController, logger)
 
-    fun attach(listBuilder: ShadeListBuilder) {
-        listBuilder.setOnRenderListListener(::onNewNotifTree)
-    }
+    fun attach(listBuilder: ShadeListBuilder) =
+            listBuilder.setOnRenderListListener(::onNewNotifTree)
 
-    private fun onNewNotifTree(tree: List<ListEntry>) {
-        viewDiffer.applySpec(buildTree(tree))
-    }
+    private fun onNewNotifTree(tree: List<ListEntry>) = viewDiffer.applySpec(buildTree(tree))
 
     private fun buildTree(notifList: List<ListEntry>): NodeSpec {
-        val root = NodeSpecImpl(null, rootController)
-
-        for (entry in notifList) {
-            // TODO: Add section header logic here
-            root.children.add(buildNotifNode(entry, root))
+        val root = NodeSpecImpl(null, rootController).apply {
+            // Insert first section header, if present
+            notifList.firstOrNull()?.section?.headerController?.let {
+                children.add(NodeSpecImpl(this, it))
+            }
+            notifList.asSequence().zipWithNext().forEach { (prev, entry) ->
+                // Insert new header if the section has changed between two entries
+                entry.section.takeIf { it != prev.section }?.headerController?.let {
+                    children.add(NodeSpecImpl(this, it))
+                }
+                children.add(buildNotifNode(entry, this))
+            }
         }
-
         notificationIconAreaController.updateNotificationIcons(notifList)
         return root
     }
 
-    private fun buildNotifNode(entry: ListEntry, parent: NodeSpec): NodeSpec {
-        return when (entry) {
-            is NotificationEntry -> {
-                NodeSpecImpl(parent, viewBarn.requireView(entry))
-            }
-            is GroupEntry -> {
-                val groupNode = NodeSpecImpl(
-                        parent,
-                        viewBarn.requireView(checkNotNull(entry.summary)))
-
-                for (childEntry in entry.children) {
-                    groupNode.children.add(buildNotifNode(childEntry, groupNode))
-                }
-
-                groupNode
-            }
-            else -> {
-                throw RuntimeException("Unexpected entry: $entry")
-            }
-        }
+    private fun buildNotifNode(entry: ListEntry, parent: NodeSpec): NodeSpec = when (entry) {
+        is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireView(entry))
+        is GroupEntry -> NodeSpecImpl(parent, viewBarn.requireView(checkNotNull(entry.summary)))
+                .apply { entry.children.forEach { children.add(buildNotifNode(it, this)) } }
+        else -> throw RuntimeException("Unexpected entry: $entry")
     }
 }
 
@@ -91,12 +79,11 @@
     private val viewBarn: NotifViewBarn,
     private val notificationIconAreaController: NotificationIconAreaController
 ) {
-    fun create(listContainer: NotificationListContainer): ShadeViewManager {
-        return ShadeViewManager(
-                context,
-                listContainer,
-                logger,
-                viewBarn,
-                notificationIconAreaController)
-    }
+    fun create(listContainer: NotificationListContainer) =
+            ShadeViewManager(
+                    context,
+                    listContainer,
+                    logger,
+                    viewBarn,
+                    notificationIconAreaController)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
new file mode 100644
index 0000000..179d49c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.systemui.statusbar.notification.dagger
+
+import android.annotation.StringRes
+import android.content.Intent
+import android.provider.Settings
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderNodeControllerImpl
+import dagger.Binds
+import dagger.BindsInstance
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import javax.inject.Provider
+import javax.inject.Qualifier
+import javax.inject.Scope
+
+@Module(subcomponents = [SectionHeaderControllerSubcomponent::class])
+object NotificationSectionHeadersModule {
+
+    @Provides
+    @HeaderClick
+    @JvmStatic fun providesOnHeaderClickListener(
+        activityStarter: ActivityStarter
+    ) = View.OnClickListener {
+        activityStarter.startActivity(
+                Intent(Settings.ACTION_NOTIFICATION_SETTINGS),
+                true /* onlyProvisioned */,
+                true /* dismissShade */,
+                Intent.FLAG_ACTIVITY_SINGLE_TOP)
+    }
+
+    @Provides
+    @IncomingHeader
+    @SysUISingleton
+    @JvmStatic fun providesIncomingHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("incoming header")
+            .headerText(R.string.notification_section_header_incoming)
+            .build()
+
+    @Provides
+    @AlertingHeader
+    @SysUISingleton
+    @JvmStatic fun providesAlertingHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("alerting header")
+            .headerText(R.string.notification_section_header_alerting)
+            .build()
+
+    @Provides
+    @PeopleHeader
+    @SysUISingleton
+    @JvmStatic fun providesPeopleHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("people header")
+            .headerText(R.string.notification_section_header_conversations)
+            .build()
+
+    @Provides
+    @SilentHeader
+    @SysUISingleton
+    @JvmStatic fun providesSilentHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("silent header")
+            .headerText(R.string.notification_section_header_gentle)
+            .build()
+
+    @Provides
+    @SilentHeader
+    @JvmStatic fun providesSilentHeaderNodeController(
+        @SilentHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @SilentHeader
+    @JvmStatic fun providesSilentHeaderController(
+        @SilentHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+
+    @Provides
+    @AlertingHeader
+    @JvmStatic fun providesAlertingHeaderNodeController(
+        @AlertingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @AlertingHeader
+    @JvmStatic fun providesAlertingHeaderController(
+        @AlertingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+
+    @Provides
+    @PeopleHeader
+    @JvmStatic fun providesPeopleHeaderNodeController(
+        @PeopleHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @PeopleHeader
+    @JvmStatic fun providesPeopleHeaderController(
+        @PeopleHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+
+    @Provides
+    @IncomingHeader
+    @JvmStatic fun providesIncomingHeaderNodeController(
+        @IncomingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @IncomingHeader
+    @JvmStatic fun providesIncomingHeaderController(
+        @IncomingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+}
+
+@Subcomponent(modules = [ SectionHeaderBindingModule::class ])
+@SectionHeaderScope
+interface SectionHeaderControllerSubcomponent {
+
+    val nodeController: NodeController
+    val headerController: SectionHeaderController
+
+    @Subcomponent.Builder
+    interface Builder {
+        fun build(): SectionHeaderControllerSubcomponent
+        @BindsInstance fun nodeLabel(@NodeLabel nodeLabel: String): Builder
+        @BindsInstance fun headerText(@HeaderText @StringRes headerText: Int): Builder
+    }
+}
+
+@Module
+private abstract class SectionHeaderBindingModule {
+    @Binds abstract fun bindsNodeController(impl: SectionHeaderNodeControllerImpl): NodeController
+    @Binds abstract fun bindsSectionHeaderController(
+        impl: SectionHeaderNodeControllerImpl
+    ): SectionHeaderController
+}
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class HeaderText
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class IncomingHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class AlertingHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class SilentHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class PeopleHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class NodeLabel
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class HeaderClick
+
+@Scope
+@Retention(AnnotationRetention.BINARY)
+annotation class SectionHeaderScope
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ea86d253..2327063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -88,7 +88,7 @@
 /**
  * Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
  */
-@Module
+@Module(includes = { NotificationSectionHeadersModule.class })
 public interface NotificationsModule {
     /** Provides an instance of {@link NotificationEntryManager} */
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index ff7793d..4f7e14b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -18,23 +18,21 @@
 import android.annotation.ColorInt
 import android.annotation.IntDef
 import android.annotation.LayoutRes
-import android.content.Intent
-import android.provider.Settings
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.media.KeyguardMediaController
-import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.people.DataListener
-import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter
-import com.android.systemui.statusbar.notification.people.PeopleHubViewBoundary
-import com.android.systemui.statusbar.notification.people.PersonViewModel
-import com.android.systemui.statusbar.notification.people.Subscription
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
+import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager
+import com.android.systemui.statusbar.notification.dagger.AlertingHeader
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader
+import com.android.systemui.statusbar.notification.dagger.PeopleHeader
+import com.android.systemui.statusbar.notification.dagger.SilentHeader
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
@@ -46,19 +44,25 @@
 import javax.inject.Inject
 
 /**
- * Manages the boundaries of the two notification sections (high priority and low priority). Also
- * shows/hides the headers for those sections where appropriate.
+ * Manages the boundaries of the notification sections (incoming, conversations, high priority, and
+ * low priority).
+ *
+ * In the legacy notification pipeline, this is responsible for correctly positioning all section
+ * headers after the [NotificationStackScrollLayout] has had notifications added/removed/changed. In
+ * the new pipeline, this is handled as part of the [ShadeViewManager].
  *
  * TODO: Move remaining sections logic from NSSL into this class.
  */
 class NotificationSectionsManager @Inject internal constructor(
-    private val activityStarter: ActivityStarter,
     private val statusBarStateController: StatusBarStateController,
     private val configurationController: ConfigurationController,
-    private val peopleHubViewAdapter: PeopleHubViewAdapter,
     private val keyguardMediaController: KeyguardMediaController,
     private val sectionsFeatureManager: NotificationSectionsFeatureManager,
-    private val logger: NotificationSectionsLogger
+    private val logger: NotificationSectionsLogger,
+    @IncomingHeader private val incomingHeaderController: SectionHeaderController,
+    @PeopleHeader private val peopleHeaderController: SectionHeaderController,
+    @AlertingHeader private val alertingHeaderController: SectionHeaderController,
+    @SilentHeader private val silentHeaderController: SectionHeaderController
 ) : SectionProvider {
 
     private val configurationListener = object : ConfigurationController.ConfigurationListener {
@@ -67,46 +71,25 @@
         }
     }
 
-    private val peopleHubViewBoundary: PeopleHubViewBoundary = object : PeopleHubViewBoundary {
-        override fun setVisible(isVisible: Boolean) {
-            if (peopleHubVisible != isVisible) {
-                peopleHubVisible = isVisible
-                if (initialized) {
-                    updateSectionBoundaries("PeopleHub visibility changed")
-                }
-            }
-        }
-
-        override val associatedViewForClickAnimation: View
-            get() = peopleHeaderView!!
-
-        override val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
-            get() = peopleHeaderView!!.personViewAdapters
-    }
-
     private lateinit var parent: NotificationStackScrollLayout
     private var initialized = false
     private var onClearSilentNotifsClickListener: View.OnClickListener? = null
 
-    @get:VisibleForTesting
-    var silentHeaderView: SectionHeaderView? = null
-        private set
+    @VisibleForTesting
+    val silentHeaderView: SectionHeaderView?
+        get() = silentHeaderController.headerView
 
-    @get:VisibleForTesting
-    var alertingHeaderView: SectionHeaderView? = null
-        private set
+    @VisibleForTesting
+    val alertingHeaderView: SectionHeaderView?
+        get() = alertingHeaderController.headerView
 
-    @get:VisibleForTesting
-    var incomingHeaderView: SectionHeaderView? = null
-        private set
+    @VisibleForTesting
+    val incomingHeaderView: SectionHeaderView?
+        get() = incomingHeaderController.headerView
 
-    @get:VisibleForTesting
-    var peopleHeaderView: PeopleHubView? = null
-        private set
-
-    @set:VisibleForTesting
-    var peopleHubVisible = false
-    private var peopleHubSubscription: Subscription? = null
+    @VisibleForTesting
+    val peopleHeaderView: SectionHeaderView?
+        get() = peopleHeaderController.headerView
 
     @get:VisibleForTesting
     var mediaControlsView: MediaHeaderView? = null
@@ -150,34 +133,10 @@
      * Reinflates the entire notification header, including all decoration views.
      */
     fun reinflateViews(layoutInflater: LayoutInflater) {
-        silentHeaderView = reinflateView(
-                silentHeaderView, layoutInflater, R.layout.status_bar_notification_section_header
-        ).apply {
-            setHeaderText(R.string.notification_section_header_gentle)
-            setOnHeaderClickListener { onGentleHeaderClick() }
-            setOnClearAllClickListener { onClearGentleNotifsClick(it) }
-        }
-        alertingHeaderView = reinflateView(
-                alertingHeaderView, layoutInflater, R.layout.status_bar_notification_section_header
-        ).apply {
-            setHeaderText(R.string.notification_section_header_alerting)
-            setOnHeaderClickListener { onGentleHeaderClick() }
-        }
-        peopleHubSubscription?.unsubscribe()
-        peopleHubSubscription = null
-        peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip)
-                .apply {
-                    setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() })
-                }
-        if (ENABLE_SNOOZED_CONVERSATION_HUB) {
-            peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary)
-        }
-        incomingHeaderView = reinflateView(
-                incomingHeaderView, layoutInflater, R.layout.status_bar_notification_section_header
-        ).apply {
-            setHeaderText(R.string.notification_section_header_incoming)
-            setOnHeaderClickListener { onGentleHeaderClick() }
-        }
+        silentHeaderController.reinflateView(parent)
+        alertingHeaderController.reinflateView(parent)
+        peopleHeaderController.reinflateView(parent)
+        incomingHeaderController.reinflateView(parent)
         mediaControlsView =
                 reinflateView(mediaControlsView, layoutInflater, R.layout.keyguard_media_header)
                         .also(keyguardMediaController::attach)
@@ -296,7 +255,6 @@
         // target, but won't be once they are moved / removed after the pass has completed.
 
         val showHeaders = statusBarStateController.state != StatusBarState.KEYGUARD
-        val usingPeopleFiltering = sectionsFeatureManager.isFilteringEnabled()
         val usingMediaControls = sectionsFeatureManager.isMediaControlsEnabled()
 
         val mediaState = mediaControlsView?.let(::expandableViewHeaderState)
@@ -319,7 +277,6 @@
         ).filterNotNull()
 
         var peopleNotifsPresent = false
-        var lastNotifIndex = 0
         var nextBucket: Int? = null
         var inIncomingSection = false
 
@@ -373,30 +330,9 @@
 
             // Check if there are any people notifications
             peopleNotifsPresent = peopleNotifsPresent || row.entry.bucket == BUCKET_PEOPLE
-
-            if (nextBucket == null) {
-                lastNotifIndex = i
-            }
             nextBucket = row.entry.bucket
         }
 
-        if (showHeaders && usingPeopleFiltering && peopleHubVisible) {
-            peopleState?.targetPosition = peopleState?.targetPosition
-                    // Insert the people header even if there are no people visible, in order to
-                    // show the hub. Put it directly above the next header.
-                    ?: alertingState?.targetPosition
-                    ?: gentleState?.targetPosition
-                    // Put it at the end of the list.
-                    ?: lastNotifIndex
-
-            // Offset the target to account for the current position of the people header.
-            peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
-                peopleState.targetPosition?.let { target ->
-                    if (current < target) target - 1 else target
-                }
-            }
-        }
-
         mediaState?.targetPosition = if (usingMediaControls) 0 else null
 
         logger.logStr("New header target positions:")
@@ -420,14 +356,6 @@
                     .hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
             setAreThereDismissableGentleNotifs(hasActiveClearableNotifications)
         }
-        peopleHeaderView?.run {
-            canSwipe = showHeaders && peopleHubVisible && !peopleNotifsPresent
-            peopleState?.targetPosition?.let { targetPosition ->
-                if (targetPosition != peopleState.currentPosition) {
-                    resetTranslation()
-                }
-            }
-        }
     }
 
     private sealed class SectionBounds {
@@ -513,31 +441,13 @@
         }
     }
 
-    private fun onGentleHeaderClick() {
-        val intent = Intent(Settings.ACTION_NOTIFICATION_SETTINGS)
-        activityStarter.startActivity(
-                intent,
-                true,
-                true,
-                Intent.FLAG_ACTIVITY_SINGLE_TOP)
-    }
-
-    private fun onClearGentleNotifsClick(v: View) {
-        onClearSilentNotifsClickListener?.onClick(v)
-    }
-
     /** Listener for when the "clear all" button is clicked on the gentle notification header. */
     fun setOnClearSilentNotifsClickListener(listener: View.OnClickListener) {
         onClearSilentNotifsClickListener = listener
     }
 
-    fun hidePeopleRow() {
-        peopleHubVisible = false
-        updateSectionBoundaries("PeopleHub dismissed")
-    }
-
     fun setHeaderForegroundColor(@ColorInt color: Int) {
-        peopleHeaderView?.setTextColor(color)
+        peopleHeaderView?.setForegroundColor(color)
         silentHeaderView?.setForegroundColor(color)
         alertingHeaderView?.setForegroundColor(color)
     }
@@ -545,7 +455,6 @@
     companion object {
         private const val TAG = "NotifSectionsManager"
         private const val DEBUG = false
-        private const val ENABLE_SNOOZED_CONVERSATION_HUB = false
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b33aa57..e0e3013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,14 +16,10 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
-import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -49,10 +45,8 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -83,8 +77,6 @@
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
 import com.android.keyguard.KeyguardSliceView;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
@@ -96,27 +88,20 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -136,7 +121,6 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
-import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.Assert;
 
 import java.io.FileDescriptor;
@@ -173,7 +157,6 @@
      */
     private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
     private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
-    private final DynamicPrivacyController mDynamicPrivacyController;
     private final SysuiStatusBarStateController mStatusbarStateController;
 
     private ExpandHelper mExpandHelper;
@@ -225,7 +208,6 @@
     private int mBottomMargin;
     private int mBottomInset = 0;
     private float mQsExpansionFraction;
-    private int mCurrentUserId;
 
     /**
      * The algorithm which calculates the properties for our children
@@ -468,12 +450,7 @@
     private int mHeadsUpInset;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private final Rect mTmpRect = new Rect();
-    private final FeatureFlags mFeatureFlags;
-    private final NotifPipeline mNotifPipeline;
-    private final NotifCollection mNotifCollection;
-    private final NotificationEntryManager mEntryManager;
-    private final IStatusBarService mBarService = IStatusBarService.Stub.asInterface(
-            ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    private DismissAllAnimationListener mDismissAllAnimationListener;
     @VisibleForTesting
     protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     protected final UiEventLogger mUiEventLogger;
@@ -553,18 +530,13 @@
             @Named(VIEW_CONTEXT) Context context,
             AttributeSet attrs,
             NotificationRoundnessManager notificationRoundnessManager,
-            DynamicPrivacyController dynamicPrivacyController,
             SysuiStatusBarStateController statusbarStateController,
             NotificationSectionsManager notificationSectionsManager,
             ForegroundServiceSectionController fgsSectionController,
             ForegroundServiceDismissalFeatureController fgsFeatureController,
-            FeatureFlags featureFlags,
-            NotifPipeline notifPipeline,
-            NotificationEntryManager entryManager,
-            NotifCollection notifCollection,
-            UiEventLogger uiEventLogger,
             GroupMembershipManager groupMembershipManager,
-            GroupExpansionManager groupExpansionManager
+            GroupExpansionManager groupExpansionManager,
+            UiEventLogger uiEventLogger
     ) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
@@ -576,7 +548,8 @@
         mSectionsManager.initialize(this, LayoutInflater.from(context));
         mSectionsManager.setOnClearSilentNotifsClickListener(v -> {
             // Leave the shade open if there will be other notifs left over to clear
-            final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
+            final boolean closeShade =
+                    !mController.hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
             clearNotifications(ROWS_GENTLE, closeShade);
         });
         mSections = mSectionsManager.createSectionsForBuckets();
@@ -613,37 +586,8 @@
             mDebugPaint.setTextSize(25f);
         }
         mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
-
-        TunerService tunerService = Dependency.get(TunerService.class);
-        tunerService.addTunable((key, newValue) -> {
-            if (key.equals(HIGH_PRIORITY)) {
-                mHighPriorityBeforeSpeedBump = "1".equals(newValue);
-            }
-        }, HIGH_PRIORITY);
-
-        mFeatureFlags = featureFlags;
-        mNotifPipeline = notifPipeline;
-        mEntryManager = entryManager;
-        mNotifCollection = notifCollection;
-        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
-                @Override
-                public void onEntryUpdated(NotificationEntry entry) {
-                    NotificationStackScrollLayout.this.onEntryUpdated(entry);
-                }
-            });
-        } else {
-            mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
-                @Override
-                public void onPreEntryUpdated(NotificationEntry entry) {
-                    NotificationStackScrollLayout.this.onEntryUpdated(entry);
-                }
-            });
-        }
         mGroupMembershipManager = groupMembershipManager;
         mGroupExpansionManager = groupExpansionManager;
-
-        mDynamicPrivacyController = dynamicPrivacyController;
         mStatusbarStateController = statusbarStateController;
         initializeForegroundServiceSection(fgsFeatureController);
         mUiEventLogger = uiEventLogger;
@@ -707,10 +651,13 @@
         if (mFooterView == null) {
             return;
         }
-        boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
-        boolean showFooterView = (showDismissView || hasActiveNotifications())
+        // TODO: move this logic to controller, which will invoke updateFooterView directly
+        boolean showDismissView = mClearAllEnabled &&
+                mController.hasActiveClearableNotifications(ROWS_ALL);
+        RemoteInputController remoteInputController = mRemoteInputManager.getController();
+        boolean showFooterView = (showDismissView || mController.hasActiveNotifications())
                 && mStatusBarState != StatusBarState.KEYGUARD
-                && !mRemoteInputManager.getController().isRemoteInputActive();
+                && (remoteInputController == null || !remoteInputController.isRemoteInputActive());
         boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
 
@@ -721,22 +668,8 @@
      * Return whether there are any clearable notifications
      */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
-        if (mDynamicPrivacyController.isInLockedDownShade()) {
-            return false;
-        }
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (!(child instanceof ExpandableNotificationRow)) {
-                continue;
-            }
-            final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            if (row.canViewBeDismissed() && matchesSelection(row, selection)) {
-                return true;
-            }
-        }
-        return false;
+    boolean hasActiveClearableNotifications(@SelectedRows int selection) {
+        return mController.hasActiveClearableNotifications(selection);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5292,8 +5225,6 @@
         updateFooter();
         requestChildrenUpdate();
         onUpdateRowStates();
-
-        mEntryManager.updateNotifications("StatusBar state changed");
         updateVisibility();
     }
 
@@ -5697,10 +5628,6 @@
         return mController;
     }
 
-    void setCurrentUserid(int userId) {
-        mCurrentUserId = userId;
-    }
-
     void addSwipedOutView(View v) {
         mSwipedOutViews.add(v);
     }
@@ -5774,6 +5701,14 @@
         return mCheckForLeavebehind;
     }
 
+    void setDismissAllAnimationListener(DismissAllAnimationListener dismissAllAnimationListener) {
+        mDismissAllAnimationListener = dismissAllAnimationListener;
+    }
+
+    public void setHighPriorityBeforeSpeedBump(boolean highPriorityBeforeSpeedBump) {
+        mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
@@ -5852,7 +5787,7 @@
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
-    private static boolean matchesSelection(
+    static boolean matchesSelection(
             ExpandableNotificationRow row,
             @SelectedRows int selection) {
         switch (selection) {
@@ -6127,7 +6062,7 @@
 
     // --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
 
-    private void onEntryUpdated(NotificationEntry entry) {
+    void onEntryUpdated(NotificationEntry entry) {
         // If the row already exists, the user may have performed a dismiss action on the
         // notification. Since it's not clearable we should snap it back.
         if (entry.rowExists() && !entry.getSbn().isClearable()) {
@@ -6135,73 +6070,17 @@
         }
     }
 
-    private boolean hasActiveNotifications() {
-        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            return !mNotifPipeline.getShadeList().isEmpty();
-        } else {
-            return mEntryManager.hasActiveNotifications();
-        }
-    }
-
     /**
      * Called after the animations for a "clear all notifications" action has ended.
      */
     private void onDismissAllAnimationsEnd(
             List<ExpandableNotificationRow> viewsToRemove,
             @SelectedRows int selectedRows) {
-        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            if (selectedRows == ROWS_ALL) {
-                mNotifCollection.dismissAllNotifications(mCurrentUserId);
-            } else {
-                final List<Pair<NotificationEntry, DismissedByUserStats>>
-                        entriesWithRowsDismissedFromShade = new ArrayList<>();
-                final int numVisibleEntries = mNotifPipeline.getShadeListCount();
-                for (int i = 0; i < viewsToRemove.size(); i++) {
-                    final NotificationEntry entry = viewsToRemove.get(i).getEntry();
-                    entriesWithRowsDismissedFromShade.add(
-                            new Pair<NotificationEntry, DismissedByUserStats>(
-                                    entry,
-                                    getDismissedByUserStats(entry, numVisibleEntries)));
-                }
-                mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
-            }
-        } else {
-            for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
-                if (canChildBeDismissed(rowToRemove)) {
-                    mEntryManager.performRemoveNotification(
-                            rowToRemove.getEntry().getSbn(),
-                            getDismissedByUserStats(
-                                    rowToRemove.getEntry(),
-                                    mEntryManager.getActiveNotificationsCount()),
-                            NotificationListenerService.REASON_CANCEL_ALL);
-                } else {
-                    rowToRemove.resetTranslation();
-                }
-            }
-            if (selectedRows == ROWS_ALL) {
-                try {
-                    mBarService.onClearAllNotifications(mCurrentUserId);
-                } catch (Exception ex) {
-                }
-            }
+        if (mDismissAllAnimationListener != null) {
+            mDismissAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
         }
     }
 
-    private DismissedByUserStats getDismissedByUserStats(
-            NotificationEntry entry,
-            int numVisibleEntries
-    ) {
-        return new DismissedByUserStats(
-            DISMISSAL_SHADE,
-            DISMISS_SENTIMENT_NEUTRAL,
-            NotificationVisibility.obtain(
-                    entry.getKey(),
-                    entry.getRanking().getRank(),
-                    numVisibleEntries,
-                    true,
-                    NotificationLogger.getNotificationLocation(entry)));
-    }
-
     public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) {
         mKeyguardMediaControllorVisible = keyguardMediaControllorVisible;
     }
@@ -6218,7 +6097,8 @@
         /* Only ever called as a consequence of a lockscreen expansion gesture. */
         @Override
         public boolean onDraggedDown(View startingChild, int dragLengthY) {
-            boolean canDragDown = hasActiveNotifications() || mKeyguardMediaControllorVisible;
+            boolean canDragDown =
+                    mController.hasActiveNotifications() || mKeyguardMediaControllorVisible;
             if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
                 mLockscreenGestureLogger.write(
                         MetricsEvent.ACTION_LS_SHADE,
@@ -6236,7 +6116,7 @@
                 }
 
                 return true;
-            } else if (mDynamicPrivacyController.isInLockedDownShade()) {
+            } else if (mController.isInLockedDownShade()) {
                 mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
                 mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
                         null /* cancelRunnable */, false /* afterKeyguardGone */);
@@ -6280,7 +6160,7 @@
             if (isDragDownAnywhereEnabled()) {
                 return true;
             }
-            if (mDynamicPrivacyController.isInLockedDownShade()) {
+            if (mController.isInLockedDownShade()) {
                 if (view == null) {
                     // Dragging down is allowed in general
                     return true;
@@ -6424,7 +6304,7 @@
     /** Enum for selecting some or all notification rows (does not included non-notif views). */
     @Retention(SOURCE)
     @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
-    public @interface SelectedRows {}
+    @interface SelectedRows {}
     /** All rows representing notifs. */
     public static final int ROWS_ALL = 0;
     /** Only rows where entry.isHighPriority() is true. */
@@ -6466,4 +6346,9 @@
     interface KeyguardBypassEnabledProvider {
         boolean getBypassEnabled();
     }
+
+    interface DismissAllAnimationListener {
+        void onAnimationEnd(
+                List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 81e6258..88b7fab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -16,15 +16,28 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
+import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
 
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Display;
 import android.view.MotionEvent;
 import android.view.View;
@@ -35,6 +48,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
@@ -47,6 +62,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
 import com.android.systemui.statusbar.NotificationShelfController;
@@ -56,11 +72,20 @@
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.ShadeViewRefactor;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
+import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -82,6 +107,8 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.BiConsumer;
 
 import javax.inject.Inject;
@@ -110,6 +137,11 @@
     private final Resources mResources;
     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
     private final ScrimController mScrimController;
+    private final FeatureFlags mFeatureFlags;
+    private final NotifPipeline mNotifPipeline;
+    private final NotifCollection mNotifCollection;
+    private final NotificationEntryManager mNotificationEntryManager;
+    private final IStatusBarService mIStatusBarService;
     private final KeyguardMediaController mKeyguardMediaController;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final KeyguardBypassController mKeyguardBypassController;
@@ -117,6 +149,7 @@
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     // TODO: StatusBar should be encapsulated behind a Controller
     private final StatusBar mStatusBar;
+    private final SectionHeaderController mSilentHeaderController;
 
     private NotificationStackScrollLayout mView;
     private boolean mFadeNotificationsOnDismiss;
@@ -206,13 +239,13 @@
                     mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
                             mLockscreenUserManager.isAnyProfilePublicMode());
                     mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
+                    mNotificationEntryManager.updateNotifications("StatusBar state changed");
                 }
             };
 
     private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
         @Override
         public void onUserChanged(int userId) {
-            mView.setCurrentUserid(userId);
             mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
         }
     };
@@ -359,10 +392,6 @@
                                 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
                     }
 
-                    if (view instanceof PeopleHubView) {
-                        mNotificationSectionsManager.hidePeopleRow();
-                    }
-
                     if (!isBlockingHelperShown) {
                         mView.addSwipedOutView(view);
                     }
@@ -523,7 +552,13 @@
             StatusBar statusBar,
             ScrimController scrimController,
             NotificationGroupManagerLegacy legacyGroupManager,
-            GroupExpansionManager groupManager) {
+            GroupExpansionManager groupManager,
+            @SilentHeader SectionHeaderController silentHeaderController,
+            FeatureFlags featureFlags,
+            NotifPipeline notifPipeline,
+            NotifCollection notifCollection,
+            NotificationEntryManager notificationEntryManager,
+            IStatusBarService iStatusBarService) {
         mAllowLongPress = allowLongPress;
         mNotificationGutsManager = notificationGutsManager;
         mHeadsUpManager = headsUpManager;
@@ -547,7 +582,6 @@
         groupManager.registerGroupExpansionChangeListener((changedRow, expanded) -> {
             mView.onGroupExpandChanged(changedRow, expanded);
         });
-
         legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
             @Override
             public void onGroupCreatedFromChildren(NotificationGroup group) {
@@ -559,6 +593,12 @@
                 mStatusBar.requestNotificationUpdate("onGroupsChanged");
             }
         });
+        mSilentHeaderController = silentHeaderController;
+        mFeatureFlags = featureFlags;
+        mNotifPipeline = notifPipeline;
+        mNotifCollection = notifCollection;
+        mNotificationEntryManager = notificationEntryManager;
+        mIStatusBarService = iStatusBarService;
     }
 
     public void attach(NotificationStackScrollLayout view) {
@@ -566,6 +606,7 @@
         mView.setController(this);
         mView.setTouchHandler(new TouchHandler());
         mView.setStatusBar(mStatusBar);
+        mView.setDismissAllAnimationListener(this::onAnimationEnd);
 
         mSwipeHelper = mNotificationSwipeHelperBuilder
                 .setSwipeDirection(SwipeHelper.X)
@@ -573,6 +614,22 @@
                 .setOnMenuEventListener(mMenuEventListener)
                 .build();
 
+        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+                @Override
+                public void onEntryUpdated(NotificationEntry entry) {
+                    mView.onEntryUpdated(entry);
+                }
+            });
+        } else {
+            mNotificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+                @Override
+                public void onPreEntryUpdated(NotificationEntry entry) {
+                    mView.onEntryUpdated(entry);
+                }
+            });
+        }
+
         mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
                 mSwipeHelper);
 
@@ -584,7 +641,6 @@
         mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
 
         mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
-        mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId());
 
         mFadeNotificationsOnDismiss =  // TODO: this should probably be injected directly
                 mResources.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
@@ -594,12 +650,19 @@
 
         mTunerService.addTunable(
                 (key, newValue) -> {
-                    if (key.equals(Settings.Secure.NOTIFICATION_DISMISS_RTL)) {
-                        mView.updateDismissRtlSetting("1".equals(newValue));
-                    } else if (key.equals(Settings.Secure.NOTIFICATION_HISTORY_ENABLED)) {
-                        updateFooter();
+                    switch (key) {
+                        case Settings.Secure.NOTIFICATION_DISMISS_RTL:
+                            mView.updateDismissRtlSetting("1".equals(newValue));
+                            break;
+                        case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
+                            updateFooter();
+                            break;
+                        case HIGH_PRIORITY:
+                            mView.setHighPriorityBeforeSpeedBump("1".equals(newValue));
+                            break;
                     }
                 },
+                HIGH_PRIORITY,
                 Settings.Secure.NOTIFICATION_DISMISS_RTL,
                 Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
 
@@ -624,6 +687,7 @@
             mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
         }
         mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+        mSilentHeaderController.setOnClearAllClickListener(v -> clearSilentNotifications());
     }
 
     public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
@@ -680,12 +744,12 @@
     }
 
     public void setOverscrollTopChangedListener(
-            NotificationStackScrollLayout.OnOverscrollTopChangedListener listener) {
+            OnOverscrollTopChangedListener listener) {
         mView.setOverscrollTopChangedListener(listener);
     }
 
     public void setOnEmptySpaceClickListener(
-            NotificationStackScrollLayout.OnEmptySpaceClickListener listener) {
+            OnEmptySpaceClickListener listener) {
         mView.setOnEmptySpaceClickListener(listener);
     }
 
@@ -1003,9 +1067,26 @@
         mView.setPulsing(pulsing, animatePulse);
     }
 
-    public boolean hasActiveClearableNotifications(
-            @NotificationStackScrollLayout.SelectedRows int selection) {
-        return mView.hasActiveClearableNotifications(selection);
+    /**
+     * Return whether there are any clearable notifications
+     */
+    public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
+        if (mDynamicPrivacyController.isInLockedDownShade()) {
+            return false;
+        }
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            if (row.canViewBeDismissed() &&
+                    NotificationStackScrollLayout.matchesSelection(row, selection)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -1116,6 +1197,29 @@
         mView.resetCheckSnoozeLeavebehind();
     }
 
+    private DismissedByUserStats getDismissedByUserStats(
+            NotificationEntry entry,
+            int numVisibleEntries
+    ) {
+        return new DismissedByUserStats(
+                DISMISSAL_SHADE,
+                DISMISS_SENTIMENT_NEUTRAL,
+                NotificationVisibility.obtain(
+                        entry.getKey(),
+                        entry.getRanking().getRank(),
+                        numVisibleEntries,
+                        true,
+                        NotificationLogger.getNotificationLocation(entry)));
+    }
+
+    boolean hasActiveNotifications() {
+        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            return !mNotifPipeline.getShadeList().isEmpty();
+        } else {
+            return mNotificationEntryManager.hasActiveNotifications();
+        }
+    }
+
     public void closeControlsIfOutsideTouch(MotionEvent ev) {
         NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
         NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
@@ -1138,6 +1242,57 @@
         }
     }
 
+    public void clearSilentNotifications() {
+        // Leave the shade open if there will be other notifs left over to clear
+        final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
+        mView.clearNotifications(ROWS_GENTLE, closeShade);
+    }
+
+    private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove, int selectedRows) {
+        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            if (selectedRows == ROWS_ALL) {
+                mNotifCollection.dismissAllNotifications(
+                        mLockscreenUserManager.getCurrentUserId());
+            } else {
+                final List<Pair<NotificationEntry, DismissedByUserStats>>
+                        entriesWithRowsDismissedFromShade = new ArrayList<>();
+                final int numVisibleEntries = mNotifPipeline.getShadeListCount();
+                for (ExpandableNotificationRow row : viewsToRemove) {
+                    final NotificationEntry entry = row.getEntry();
+                    entriesWithRowsDismissedFromShade.add(
+                            new Pair<>(
+                                    entry,
+                                    getDismissedByUserStats(entry, numVisibleEntries)));
+                }
+                mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
+            }
+        } else {
+            for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
+                if (canChildBeDismissed(rowToRemove)) {
+                    mNotificationEntryManager.performRemoveNotification(
+                            rowToRemove.getEntry().getSbn(),
+                            getDismissedByUserStats(
+                                    rowToRemove.getEntry(),
+                                    mNotificationEntryManager.getActiveNotificationsCount()),
+                            NotificationListenerService.REASON_CANCEL_ALL);
+                } else {
+                    rowToRemove.resetTranslation();
+                }
+            }
+            if (selectedRows == ROWS_ALL) {
+                try {
+                    mIStatusBarService.onClearAllNotifications(
+                            mLockscreenUserManager.getCurrentUserId());
+                } catch (Exception ignored) {
+                }
+            }
+        }
+    }
+
+    public boolean isInLockedDownShade() {
+        return mDynamicPrivacyController.isInLockedDownShade();
+    }
+
     private class NotificationListContainerImpl implements NotificationListContainer {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 5777ba1..99ec7548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -97,7 +97,7 @@
     /**
      * Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything).
      */
-    void setOnHeaderClickListener(View.OnClickListener listener) {
+    public void setOnHeaderClickListener(View.OnClickListener listener) {
         mLabelClickListener = listener;
         mLabelView.setOnClickListener(listener);
     }
@@ -112,7 +112,7 @@
     }
 
     /** Fired when the user clicks on the "X" button on the far right of the header. */
-    void setOnClearAllClickListener(View.OnClickListener listener) {
+    public void setOnClearAllClickListener(View.OnClickListener listener) {
         mOnClearClickListener = listener;
         mClearAllButton.setOnClickListener(listener);
     }
@@ -122,7 +122,8 @@
         return true;
     }
 
-    void setHeaderText(@StringRes int resId) {
+    /** Sets text to be displayed in the header */
+    public void setHeaderText(@StringRes int resId) {
         mLabelTextId = resId;
         mLabelView.setText(resId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 09034c0..3665c39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -25,26 +25,21 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
-import android.util.MathUtils;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardHostView;
+import com.android.keyguard.KeyguardHostViewController;
+import com.android.keyguard.KeyguardRootViewController;
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.ContainerView;
-import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
+import com.android.keyguard.dagger.RootView;
 import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -57,18 +52,16 @@
 /**
  * A class which manages the bouncer on the lockscreen.
  */
-@KeyguardBouncerScope
 public class KeyguardBouncer {
 
     private static final String TAG = "KeyguardBouncer";
     static final long BOUNCER_FACE_DELAY = 1200;
-    static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
+    public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
     static final float EXPANSION_HIDDEN = 1f;
     static final float EXPANSION_VISIBLE = 0f;
 
     protected final Context mContext;
     protected final ViewMediatorCallback mCallback;
-    protected final LockPatternUtils mLockPatternUtils;
     protected final ViewGroup mContainer;
     private final FalsingManager mFalsingManager;
     private final DismissCallbackRegistry mDismissCallbackRegistry;
@@ -76,6 +69,8 @@
     private final BouncerExpansionCallback mExpansionCallback;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardStateController mKeyguardStateController;
+    private final KeyguardSecurityModel mKeyguardSecurityModel;
+    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -85,32 +80,33 @@
             };
     private final Runnable mRemoveViewRunnable = this::removeView;
     private final KeyguardBypassController mKeyguardBypassController;
-    protected KeyguardHostView mKeyguardView;
+    private KeyguardHostViewController mKeyguardViewController;
     private final Runnable mResetRunnable = ()-> {
-        if (mKeyguardView != null) {
-            mKeyguardView.resetSecurityContainer();
+        if (mKeyguardViewController != null) {
+            mKeyguardViewController.resetSecurityContainer();
         }
     };
 
     private int mStatusBarHeight;
     private float mExpansion = EXPANSION_HIDDEN;
     protected ViewGroup mRoot;
+    private KeyguardRootViewController mRootViewController;
     private boolean mShowingSoon;
     private int mBouncerPromptReason;
     private boolean mIsAnimatingAway;
     private boolean mIsScrimmed;
 
-    @Inject
-    public KeyguardBouncer(Context context, ViewMediatorCallback callback,
-            LockPatternUtils lockPatternUtils, @ContainerView ViewGroup container,
+    private KeyguardBouncer(Context context, ViewMediatorCallback callback,
+            ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
             BouncerExpansionCallback expansionCallback,
             KeyguardStateController keyguardStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            KeyguardBypassController keyguardBypassController, Handler handler) {
+            KeyguardBypassController keyguardBypassController, Handler handler,
+            KeyguardSecurityModel keyguardSecurityModel,
+            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
         mContext = context;
         mCallback = callback;
-        mLockPatternUtils = lockPatternUtils;
         mContainer = container;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mFalsingManager = falsingManager;
@@ -118,6 +114,8 @@
         mExpansionCallback = expansionCallback;
         mHandler = handler;
         mKeyguardStateController = keyguardStateController;
+        mKeyguardSecurityModel = keyguardSecurityModel;
+        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
         mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
         mKeyguardBypassController = keyguardBypassController;
     }
@@ -168,7 +166,7 @@
 
         // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
         // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
-        if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
+        if (allowDismissKeyguard && mKeyguardViewController.dismiss(activeUserId)) {
             return;
         }
 
@@ -204,12 +202,13 @@
      */
     private void onFullyShown() {
         mFalsingManager.onBouncerShown();
-        if (mKeyguardView == null) {
+        if (mKeyguardViewController == null) {
             Log.wtf(TAG, "onFullyShown when view was null");
         } else {
-            mKeyguardView.onResume();
+            mKeyguardViewController.onResume();
             if (mRoot != null) {
-                mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode());
+                mRoot.announceForAccessibility(
+                        mKeyguardViewController.getAccessibilityTitleForCurrentMode());
             }
         }
     }
@@ -233,28 +232,13 @@
             showPromptReason(mBouncerPromptReason);
             final CharSequence customMessage = mCallback.consumeCustomMessage();
             if (customMessage != null) {
-                mKeyguardView.showErrorMessage(customMessage);
+                mKeyguardViewController.showErrorMessage(customMessage);
             }
-            // We might still be collapsed and the view didn't have time to layout yet or still
-            // be small, let's wait on the predraw to do the animation in that case.
-            if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
-                mKeyguardView.startAppearAnimation();
-            } else {
-                mKeyguardView.getViewTreeObserver().addOnPreDrawListener(
-                        new ViewTreeObserver.OnPreDrawListener() {
-                            @Override
-                            public boolean onPreDraw() {
-                                mKeyguardView.getViewTreeObserver().removeOnPreDrawListener(this);
-                                mKeyguardView.startAppearAnimation();
-                                return true;
-                            }
-                        });
-                mKeyguardView.requestLayout();
-            }
+            mKeyguardViewController.appear(mStatusBarHeight);
             mShowingSoon = false;
             if (mExpansion == EXPANSION_VISIBLE) {
-                mKeyguardView.onResume();
-                mKeyguardView.resetSecurityContainer();
+                mKeyguardViewController.onResume();
+                mKeyguardViewController.resetSecurityContainer();
                 showPromptReason(mBouncerPromptReason);
             }
             SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
@@ -270,16 +254,16 @@
      *               and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
      */
     public void showPromptReason(int reason) {
-        if (mKeyguardView != null) {
-            mKeyguardView.showPromptReason(reason);
+        if (mKeyguardViewController != null) {
+            mKeyguardViewController.showPromptReason(reason);
         } else {
             Log.w(TAG, "Trying to show prompt reason on empty bouncer");
         }
     }
 
     public void showMessage(String message, ColorStateList colorState) {
-        if (mKeyguardView != null) {
-            mKeyguardView.showMessage(message, colorState);
+        if (mKeyguardViewController != null) {
+            mKeyguardViewController.showMessage(message, colorState);
         } else {
             Log.w(TAG, "Trying to show message on empty bouncer");
         }
@@ -293,7 +277,7 @@
 
     public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) {
         ensureView();
-        mKeyguardView.setOnDismissAction(r, cancelAction);
+        mKeyguardViewController.setOnDismissAction(r, cancelAction);
         show(false /* resetSecuritySelection */);
     }
 
@@ -307,9 +291,9 @@
         mFalsingManager.onBouncerHidden();
         mCallback.onBouncerVisiblityChanged(false /* shown */);
         cancelShowRunnable();
-        if (mKeyguardView != null) {
-            mKeyguardView.cancelDismissAction();
-            mKeyguardView.cleanUp();
+        if (mKeyguardViewController != null) {
+            mKeyguardViewController.cancelDismissAction();
+            mKeyguardViewController.cleanUp();
         }
         mIsAnimatingAway = false;
         if (mRoot != null) {
@@ -328,8 +312,8 @@
      */
     public void startPreHideAnimation(Runnable runnable) {
         mIsAnimatingAway = true;
-        if (mKeyguardView != null) {
-            mKeyguardView.startDisappearAnimation(runnable);
+        if (mKeyguardViewController != null) {
+            mKeyguardViewController.startDisappearAnimation(runnable);
         } else if (runnable != null) {
             runnable.run();
         }
@@ -345,8 +329,9 @@
     }
 
     public void onScreenTurnedOff() {
-        if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
-            mKeyguardView.onPause();
+        if (mKeyguardViewController != null
+                && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
+            mKeyguardViewController.onPause();
         }
     }
 
@@ -380,7 +365,7 @@
     }
 
     private void showPrimarySecurityScreen() {
-        mKeyguardView.showPrimarySecurityScreen();
+        mKeyguardViewController.showPrimarySecurityScreen();
     }
 
     /**
@@ -391,10 +376,8 @@
     public void setExpansion(float fraction) {
         float oldExpansion = mExpansion;
         mExpansion = fraction;
-        if (mKeyguardView != null && !mIsAnimatingAway) {
-            float alpha = MathUtils.map(ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
-            mKeyguardView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
-            mKeyguardView.setTranslationY(fraction * mKeyguardView.getHeight());
+        if (mKeyguardViewController != null && !mIsAnimatingAway) {
+            mKeyguardViewController.setExpansion(fraction);
         }
 
         if (fraction == EXPANSION_VISIBLE && oldExpansion != EXPANSION_VISIBLE) {
@@ -405,28 +388,22 @@
             mExpansionCallback.onFullyHidden();
         } else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
             mExpansionCallback.onStartingToHide();
-            if (mKeyguardView != null) {
-                mKeyguardView.onStartingToHide();
+            if (mKeyguardViewController != null) {
+                mKeyguardViewController.onStartingToHide();
             }
         }
     }
 
     public boolean willDismissWithAction() {
-        return mKeyguardView != null && mKeyguardView.hasDismissActions();
+        return mKeyguardViewController != null && mKeyguardViewController.hasDismissActions();
     }
 
     public int getTop() {
-        if (mKeyguardView == null) {
+        if (mKeyguardViewController == null) {
             return 0;
         }
 
-        int top = mKeyguardView.getTop();
-        // The password view has an extra top padding that should be ignored.
-        if (mKeyguardView.getCurrentSecurityMode() == SecurityMode.Password) {
-            View messageArea = mKeyguardView.findViewById(R.id.keyguard_message_area);
-            top += messageArea.getTop();
-        }
-        return top;
+        return mKeyguardViewController.getTop();
     }
 
     protected void ensureView() {
@@ -442,10 +419,13 @@
     protected void inflateView() {
         removeView();
         mHandler.removeCallbacks(mRemoveViewRunnable);
-        mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
-        mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view);
-        mKeyguardView.setLockPatternUtils(mLockPatternUtils);
-        mKeyguardView.setViewMediatorCallback(mCallback);
+        KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create();
+        mRootViewController = component.getKeyguardRootViewController();
+        mRootViewController.init();
+        mRoot = mRootViewController.getView();  // TODO(b/166448040): Don't access root view here.
+        mKeyguardViewController = component.getKeyguardHostViewController();
+        mKeyguardViewController.init();
+
         mContainer.addView(mRoot, mContainer.getChildCount());
         mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
                 com.android.systemui.R.dimen.status_bar_height);
@@ -465,7 +445,7 @@
     }
 
     public boolean onBackPressed() {
-        return mKeyguardView != null && mKeyguardView.handleBackKey();
+        return mKeyguardViewController != null && mKeyguardViewController.handleBackKey();
     }
 
     /**
@@ -473,7 +453,7 @@
      * notifications on Keyguard, like SIM PIN/PUK.
      */
     public boolean needsFullscreenBouncer() {
-        SecurityMode mode = Dependency.get(KeyguardSecurityModel.class).getSecurityMode(
+        SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
                 KeyguardUpdateMonitor.getCurrentUser());
         return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
     }
@@ -483,8 +463,8 @@
      * makes this method much faster.
      */
     public boolean isFullscreenBouncer() {
-        if (mKeyguardView != null) {
-            SecurityMode mode = mKeyguardView.getCurrentSecurityMode();
+        if (mKeyguardViewController != null) {
+            SecurityMode mode = mKeyguardViewController.getCurrentSecurityMode();
             return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
         }
         return false;
@@ -494,21 +474,22 @@
      * WARNING: This method might cause Binder calls.
      */
     public boolean isSecure() {
-        return mKeyguardView == null || mKeyguardView.getSecurityMode() != SecurityMode.None;
+        return mKeyguardSecurityModel.getSecurityMode(
+                KeyguardUpdateMonitor.getCurrentUser()) != SecurityMode.None;
     }
 
     public boolean shouldDismissOnMenuPressed() {
-        return mKeyguardView.shouldEnableMenuKey();
+        return mKeyguardViewController.shouldEnableMenuKey();
     }
 
     public boolean interceptMediaKey(KeyEvent event) {
         ensureView();
-        return mKeyguardView.interceptMediaKey(event);
+        return mKeyguardViewController.interceptMediaKey(event);
     }
 
     public void notifyKeyguardAuthenticated(boolean strongAuth) {
         ensureView();
-        mKeyguardView.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
+        mKeyguardViewController.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
     }
 
     public void dump(PrintWriter pw) {
@@ -516,8 +497,8 @@
         pw.println("  isShowing(): " + isShowing());
         pw.println("  mStatusBarHeight: " + mStatusBarHeight);
         pw.println("  mExpansion: " + mExpansion);
-        pw.println("  mKeyguardView; " + mKeyguardView);
-        pw.println("  mShowingSoon: " + mKeyguardView);
+        pw.println("  mKeyguardViewController; " + mKeyguardViewController);
+        pw.println("  mShowingSoon: " + mShowingSoon);
         pw.println("  mBouncerPromptReason: " + mBouncerPromptReason);
         pw.println("  mIsAnimatingAway: " + mIsAnimatingAway);
     }
@@ -528,4 +509,46 @@
         void onStartingToShow();
         void onFullyHidden();
     }
+
+    /** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
+    public static class Factory {
+        private final Context mContext;
+        private final ViewMediatorCallback mCallback;
+        private final DismissCallbackRegistry mDismissCallbackRegistry;
+        private final FalsingManager mFalsingManager;
+        private final KeyguardStateController mKeyguardStateController;
+        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+        private final KeyguardBypassController mKeyguardBypassController;
+        private final Handler mHandler;
+        private final KeyguardSecurityModel mKeyguardSecurityModel;
+        private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+
+        @Inject
+        public Factory(Context context, ViewMediatorCallback callback,
+                DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
+                KeyguardStateController keyguardStateController,
+                KeyguardUpdateMonitor keyguardUpdateMonitor,
+                KeyguardBypassController keyguardBypassController, Handler handler,
+                KeyguardSecurityModel keyguardSecurityModel,
+                KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
+            mContext = context;
+            mCallback = callback;
+            mDismissCallbackRegistry = dismissCallbackRegistry;
+            mFalsingManager = falsingManager;
+            mKeyguardStateController = keyguardStateController;
+            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+            mKeyguardBypassController = keyguardBypassController;
+            mHandler = handler;
+            mKeyguardSecurityModel = keyguardSecurityModel;
+            mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
+        }
+
+        public KeyguardBouncer create(@RootView ViewGroup container,
+                BouncerExpansionCallback expansionCallback) {
+            return new KeyguardBouncer(mContext, mCallback, container,
+                    mDismissCallbackRegistry, mFalsingManager, expansionCallback,
+                    mKeyguardStateController, mKeyguardUpdateMonitor, mKeyguardBypassController,
+                    mHandler, mKeyguardSecurityModel, mKeyguardBouncerComponentFactory);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b56993b..51209d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -44,7 +44,6 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.dagger.SysUISingleton;
@@ -104,7 +103,7 @@
     private final NavigationModeController mNavigationModeController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
-    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+    private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
         public void onFullyShown() {
@@ -216,7 +215,7 @@
             KeyguardStateController keyguardStateController,
             Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
             NotificationMediaManager notificationMediaManager,
-            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
+            KeyguardBouncer.Factory keyguardBouncerFactory) {
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -229,7 +228,7 @@
         mStatusBarStateController = sysuiStatusBarStateController;
         mDockManager = dockManager;
         mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
-        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
+        mKeyguardBouncerFactory = keyguardBouncerFactory;
     }
 
     @Override
@@ -246,9 +245,7 @@
             mLastLockVisible = mLockIconContainer.getVisibility() == View.VISIBLE;
         }
         mBiometricUnlockController = biometricUnlockController;
-        mBouncer = mKeyguardBouncerComponentFactory
-                .build(container, mExpansionCallback)
-                .createKeyguardBouncer();
+        mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
         mNotificationPanelViewController = notificationPanelViewController;
         notificationPanelViewController.addExpansionListener(this);
         mBypassController = bypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
new file mode 100644
index 0000000..64f8dbb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -0,0 +1,72 @@
+/*
+ * 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.systemui.util;
+
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+
+/**
+ * Utility class that handles view lifecycle events for View Controllers.
+ *
+ * Implementations should handle setup and teardown related activities inside of
+ * {@link #onViewAttached()} and {@link  #onViewDetached()}.
+ *
+ * @param <T> View class that this ViewController is for.
+ */
+public abstract class ViewController<T extends View> {
+    protected final T mView;
+    private boolean mInited;
+
+    private OnAttachStateChangeListener mOnAttachStateListener = new OnAttachStateChangeListener() {
+        @Override
+        public void onViewAttachedToWindow(View v) {
+            ViewController.this.onViewAttached();
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            ViewController.this.onViewDetached();
+        }
+    };
+
+    protected ViewController(T view) {
+        mView = view;
+    }
+
+    /** Call immediately after constructing Controller in order to handle view lifecycle events. */
+    public void init() {
+        if (mInited) {
+            return;
+        }
+        mInited = true;
+
+        if (mView.isAttachedToWindow()) {
+            mOnAttachStateListener.onViewAttachedToWindow(mView);
+        }
+        mView.addOnAttachStateChangeListener(mOnAttachStateListener);
+    }
+
+    /**
+     * Called when the view is attached and a call to {@link #init()} has been made in either order.
+     */
+    protected abstract void onViewAttached();
+
+    /**
+     * Called when the view is detached.
+     */
+    protected abstract void onViewDetached();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index c7a9af3..6f681e8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -38,10 +38,6 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.onehanded.OneHanded;
-import com.android.systemui.onehanded.OneHandedEvents;
-import com.android.systemui.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
-import com.android.systemui.onehanded.OneHandedTransitionCallback;
 import com.android.systemui.pip.Pip;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -49,8 +45,13 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.nano.WmShellTraceProto;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEvents;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.protolog.ShellProtoLogImpl;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
@@ -76,6 +77,9 @@
     private final Optional<Pip> mPipOptional;
     private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<OneHanded> mOneHandedOptional;
+    // Inject the organizer directly in case the optionals aren't loaded to depend on it. There
+    // are non-optional windowing features like FULLSCREEN.
+    private final ShellTaskOrganizer mShellTaskOrganizer;
     private final ProtoTracer mProtoTracer;
 
     @Inject
@@ -89,6 +93,7 @@
             Optional<Pip> pipOptional,
             Optional<SplitScreen> splitScreenOptional,
             Optional<OneHanded> oneHandedOptional,
+            ShellTaskOrganizer shellTaskOrganizer,
             ProtoTracer protoTracer) {
         super(context);
         mCommandQueue = commandQueue;
@@ -101,6 +106,7 @@
         mPipOptional = pipOptional;
         mSplitScreenOptional = splitScreenOptional;
         mOneHandedOptional = oneHandedOptional;
+        mShellTaskOrganizer = shellTaskOrganizer;
         mProtoTracer = protoTracer;
         mProtoTracer.add(this);
     }
@@ -248,10 +254,9 @@
             @Override
             public void setImeWindowStatus(int displayId, IBinder token, int vis,
                     int backDisposition, boolean showImeSwitcher) {
-                if (displayId != DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) == 0) {
-                    return;
+                if (displayId == DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) != 0) {
+                    oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
                 }
-                oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
             }
         });
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index adb9186..3faa8a7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -24,7 +24,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.onehanded.OneHanded;
 import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.pip.PipUiEventLogger;
@@ -36,6 +35,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.onehanded.OneHanded;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import dagger.BindsOptionalOf;
@@ -96,8 +96,8 @@
 
     @SysUISingleton
     @Provides
-    static ShellTaskOrganizer provideShellTaskOrganizer() {
-        ShellTaskOrganizer organizer = new ShellTaskOrganizer();
+    static ShellTaskOrganizer provideShellTaskOrganizer(TransactionPool transactionPool) {
+        ShellTaskOrganizer organizer = new ShellTaskOrganizer(transactionPool);
         organizer.registerOrganizer();
         return organizer;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 3a249d6..16fb2ca 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -24,8 +24,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.onehanded.OneHanded;
-import com.android.systemui.onehanded.OneHandedController;
 import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSurfaceTransactionHelper;
@@ -40,6 +38,8 @@
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
similarity index 61%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
rename to packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index dd5c833..54e879e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.keyguard;
@@ -19,11 +19,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.media.AudioManager;
+import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
@@ -39,41 +40,44 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class KeyguardHostViewTest extends SysuiTestCase {
+public class KeyguardHostViewControllerTest extends SysuiTestCase {
 
     @Mock
-    private KeyguardSecurityContainer mSecurityContainer;
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
-    private LockPatternUtils mLockPatternUtils;
+    private KeyguardHostView mKeyguardHostView;
+    @Mock
+    private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+    @Mock
+    private AudioManager mAudioManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private ViewMediatorCallback mViewMediatorCallback;
+
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    private KeyguardHostView mKeyguardHostView;
+    private KeyguardHostViewController mKeyguardHostViewController;
 
     @Before
     public void setup() {
-        mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
-        mKeyguardHostView = new KeyguardHostView(getContext()) {
-            @Override
-            protected void onFinishInflate() {
-                mSecurityContainer = KeyguardHostViewTest.this.mSecurityContainer;
-                mLockPatternUtils = KeyguardHostViewTest.this.mLockPatternUtils;
-            }
-        };
-        mKeyguardHostView.onFinishInflate();
+        mKeyguardHostViewController = new KeyguardHostViewController(
+                mKeyguardHostView, mKeyguardUpdateMonitor, mKeyguardSecurityContainerController,
+                mAudioManager, mTelephonyManager, mViewMediatorCallback);
     }
 
     @Test
     public void testHasDismissActions() {
-        Assert.assertFalse("Action not set yet", mKeyguardHostView.hasDismissActions());
-        mKeyguardHostView.setOnDismissAction(mock(OnDismissAction.class),
+        Assert.assertFalse("Action not set yet", mKeyguardHostViewController.hasDismissActions());
+        mKeyguardHostViewController.setOnDismissAction(mock(OnDismissAction.class),
                 null /* cancelAction */);
-        Assert.assertTrue("Action should exist", mKeyguardHostView.hasDismissActions());
+        Assert.assertTrue("Action should exist", mKeyguardHostViewController.hasDismissActions());
     }
 
     @Test
     public void testOnStartingToHide() {
-        mKeyguardHostView.onStartingToHide();
-        verify(mSecurityContainer).onStartingToHide();
+        mKeyguardHostViewController.onStartingToHide();
+        verify(mKeyguardSecurityContainerController).onStartingToHide();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1192023..d93cc05 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -182,7 +182,8 @@
         // IBiometricsFace@1.0 does not support detection, only authentication.
         when(mFaceSensorProperties.isEmpty()).thenReturn(false);
         when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorProperties(0 /* id */,
-                false /* supportsFaceDetection */, true /* supportsSelfIllumination */));
+                false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
+                1 /* maxTemplatesAllowed */));
 
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
         when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
index 58cb032..73a7ca9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
@@ -87,6 +87,8 @@
             override fun createAnimator(start: Float, end: Float) = animator
         }
         `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT))).thenReturn(INITIAL_BRIGHTNESS)
+        `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT), eq(1f)))
+                .thenReturn(INITIAL_BRIGHTNESS)
         faceAuthScreenBrightnessController.attach(whiteOverlay)
         verify(keyguardUpdateMonitor).registerCallback(capture(keyguardUpdateCallback))
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
deleted file mode 100644
index 41af53b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.systemui.onehanded;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.DisplayController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedGestureHandlerTest extends OneHandedTestCase {
-    OneHandedTouchHandler mTouchHandler;
-    OneHandedTutorialHandler mTutorialHandler;
-    OneHandedGestureHandler mGestureHandler;
-    OneHandedController mOneHandedController;
-    @Mock
-    DisplayController mMockDisplayController;
-    @Mock
-    OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mTouchHandler = new OneHandedTouchHandler();
-        mTutorialHandler = new OneHandedTutorialHandler(mContext);
-        mGestureHandler = Mockito.spy(
-                new OneHandedGestureHandler(mContext, mMockDisplayController));
-        mOneHandedController = new OneHandedController(
-                getContext(),
-                mMockDisplayController,
-                mMockDisplayAreaOrganizer,
-                mTouchHandler,
-                mTutorialHandler,
-                mGestureHandler);
-        mOneHandedController.setThreeButtonModeEnabled(true);
-    }
-
-    @Test
-    public void testOneHandedManager_registerForDisplayAreaOrganizer() {
-        verify(mMockDisplayAreaOrganizer, atLeastOnce())
-                .registerTransitionCallback(mGestureHandler);
-    }
-
-    @Test
-    public void testOneHandedManager_setGestureEventListener() {
-        OneHandedGestureHandler.OneHandedGestureEventCallback callback =
-                new OneHandedGestureHandler.OneHandedGestureEventCallback() {
-            @Override
-            public void onStart() {}
-
-            @Override
-            public void onStop() {}
-        };
-        mOneHandedController.registerGestureCallback(callback);
-
-        verify(mGestureHandler).setGestureEventListener(callback);
-        assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback);
-    }
-
-    @Test
-    public void testReceiveNewConfig_whenSetOneHandedEnabled() {
-        // 1st called at init
-        verify(mGestureHandler, atLeastOnce()).onOneHandedEnabled(true);
-        mOneHandedController.setOneHandedEnabled(true);
-        // 2nd called by setOneHandedEnabled()
-        verify(mGestureHandler, atLeast(2)).onOneHandedEnabled(true);
-    }
-
-    @Test
-    public void testOneHandedDisabled_shouldDisposeInputChannel() {
-        mOneHandedController.setOneHandedEnabled(false);
-        mOneHandedController.setSwipeToNotificationEnabled(false);
-
-        assertThat(mGestureHandler.mInputMonitor).isNull();
-        assertThat(mGestureHandler.mInputEventReceiver).isNull();
-    }
-
-    @Test
-    public void testChangeNavBarToNon3Button_shouldDisposeInputChannel() {
-        // 1st called at init
-        verify(mGestureHandler, atLeastOnce()).onOneHandedEnabled(true);
-        mOneHandedController.setOneHandedEnabled(true);
-        // 2nd called by setOneHandedEnabled()
-        verify(mGestureHandler, atLeast(2)).onOneHandedEnabled(true);
-
-        mGestureHandler.onThreeButtonModeEnabled(false);
-
-        assertThat(mGestureHandler.mInputMonitor).isNull();
-        assertThat(mGestureHandler.mInputEventReceiver).isNull();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
deleted file mode 100644
index 1e40831..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.systemui.onehanded;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.DisplayController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedTouchHandlerTest extends OneHandedTestCase {
-    OneHandedTouchHandler mTouchHandler;
-    OneHandedTutorialHandler mTutorialHandler;
-    OneHandedGestureHandler mGestureHandler;
-    OneHandedController mOneHandedController;
-    @Mock
-    DisplayController mMockDisplayController;
-    @Mock
-    OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
-        mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
-        mOneHandedController = new OneHandedController(
-                getContext(),
-                mMockDisplayController,
-                mMockDisplayAreaOrganizer,
-                mTouchHandler,
-                mTutorialHandler,
-                mGestureHandler);
-    }
-
-    @Test
-    public void testOneHandedManager_registerForDisplayAreaOrganizer() {
-        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTouchHandler);
-    }
-
-    @Test
-    public void testOneHandedManager_registerTouchEventListener() {
-        verify(mTouchHandler).registerTouchEventListener(any());
-        assertThat(mTouchHandler.mTouchEventCallback).isNotNull();
-    }
-
-    @Test
-    public void testOneHandedDisabled_shouldDisposeInputChannel() {
-        mOneHandedController.setOneHandedEnabled(false);
-        assertThat(mTouchHandler.mInputMonitor).isNull();
-        assertThat(mTouchHandler.mInputEventReceiver).isNull();
-    }
-
-    @Test
-    public void testOneHandedEnabled_monitorInputChannel() {
-        mOneHandedController.setOneHandedEnabled(true);
-        assertThat(mTouchHandler.mInputMonitor).isNotNull();
-        assertThat(mTouchHandler.mInputEventReceiver).isNotNull();
-    }
-
-    @Test
-    public void testReceiveNewConfig_whenSetOneHandedEnabled() {
-        // Called at init
-        verify(mTouchHandler, atLeastOnce()).onOneHandedEnabled(true);
-        mOneHandedController.setOneHandedEnabled(true);
-        // Called by setOneHandedEnabled()
-        verify(mTouchHandler, atLeast(2)).onOneHandedEnabled(true);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 25fb7d3..1f10d01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -32,6 +32,7 @@
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -106,6 +107,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testMicCameraChanged() {
         changeMicCamera(false) // default is true
         executor.runAllReady()
@@ -129,6 +131,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testBothChanged() {
         changeAll(true)
         changeMicCamera(false)
@@ -150,6 +153,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testMicCamera_listening() {
         changeMicCamera(true)
         executor.runAllReady()
@@ -158,6 +162,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testAllFalse_notListening() {
         changeAll(true)
         executor.runAllReady()
@@ -169,6 +174,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testSomeListening_stillListening() {
         // Mic and camera are true by default
         changeAll(true)
@@ -180,6 +186,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testAllDeleted_micCameraFalse_stopListening() {
         changeMicCamera(false)
         changeAll(true)
@@ -191,6 +198,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testMicDeleted_stillListening() {
         changeMicCamera(true)
         executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index f152a74..0a079b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -43,6 +43,7 @@
 import org.junit.Assert.assertThat
 import org.junit.Assert.assertTrue
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -271,6 +272,7 @@
     }
 
     @Test
+    @Ignore // TODO(b/168209929)
     fun testNotListeningWhenIndicatorsDisabled() {
         changeAll(false)
         changeMicCamera(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 8acb705..2ce22a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -964,6 +965,198 @@
     }
 
     @Test
+    public void testStabilizeGroupsDoesNotAllowGrouping() {
+        // GIVEN one group child without a summary yet
+        addGroupChild(0, PACKAGE_1, GROUP_1);
+
+        dispatchBuild();
+
+        // GIVEN visual stability manager doesn't allow any group changes
+        mListBuilder.setNotifStabilityManager(
+                new TestableStabilityManager().setAllowGroupChanges(false));
+
+        // WHEN we run the pipeline with the addition of a group summary & child
+        addGroupSummary(1, PACKAGE_1, GROUP_1);
+        addGroupChild(2, PACKAGE_1, GROUP_1);
+
+        dispatchBuild();
+
+        // THEN all notifications are top-level and the summary doesn't show yet
+        // because group changes aren't allowed by the stability manager
+        verifyBuiltList(
+                notif(0),
+                notif(2)
+        );
+    }
+
+    @Test
+    public void testStabilizeGroupsAllowsGroupingAllNewNotifications() {
+        // GIVEN visual stability manager doesn't allow any group changes
+        mListBuilder.setNotifStabilityManager(
+                new TestableStabilityManager().setAllowGroupChanges(false));
+
+        // WHEN we run the pipeline with all new notification groups
+        addGroupChild(0, PACKAGE_1, GROUP_1);
+        addGroupSummary(1, PACKAGE_1, GROUP_1);
+        addGroupChild(2, PACKAGE_1, GROUP_1);
+        addGroupSummary(3, PACKAGE_2, GROUP_2);
+        addGroupChild(4, PACKAGE_2, GROUP_2);
+        addGroupChild(5, PACKAGE_2, GROUP_2);
+
+        dispatchBuild();
+
+        // THEN all notifications are grouped since they're all new
+        verifyBuiltList(
+                group(
+                        summary(1),
+                        child(0),
+                        child(2)
+                ),
+                group(
+                        summary(3),
+                        child(4),
+                        child(5)
+                )
+        );
+    }
+
+
+    @Test
+    public void testStabilizeGroupsAllowsGroupingOnlyNewNotifications() {
+        // GIVEN one group child without a summary yet
+        addGroupChild(0, PACKAGE_1, GROUP_1);
+
+        dispatchBuild();
+
+        // GIVEN visual stability manager doesn't allow any group changes
+        mListBuilder.setNotifStabilityManager(
+                new TestableStabilityManager().setAllowGroupChanges(false));
+
+        // WHEN we run the pipeline with the addition of a group summary & child
+        addGroupSummary(1, PACKAGE_1, GROUP_1);
+        addGroupChild(2, PACKAGE_1, GROUP_1);
+        addGroupSummary(3, PACKAGE_2, GROUP_2);
+        addGroupChild(4, PACKAGE_2, GROUP_2);
+        addGroupChild(5, PACKAGE_2, GROUP_2);
+
+        dispatchBuild();
+
+        // THEN all notifications are top-level and the summary doesn't show yet
+        // because group changes aren't allowed by the stability manager
+        verifyBuiltList(
+                notif(0),
+                group(
+                        summary(3),
+                        child(4),
+                        child(5)
+                ),
+                notif(2)
+        );
+    }
+
+    @Test
+    public void testStabilizeGroupsHidesGroupSummary() {
+        // GIVEN one group child with a summary
+        addGroupChild(0, PACKAGE_1, GROUP_1);
+        addGroupSummary(1, PACKAGE_1, GROUP_1);
+
+        dispatchBuild(); // group summary is hidden because it needs at least 2 children to group
+
+        // GIVEN visual stability manager doesn't allow any group changes
+        mListBuilder.setNotifStabilityManager(
+                new TestableStabilityManager().setAllowGroupChanges(false));
+
+        // WHEN we run the pipeline with the addition of a child
+        addGroupChild(2, PACKAGE_1, GROUP_1);
+
+        dispatchBuild();
+
+        // THEN the children notifications are top-level and the summary still doesn't show yet
+        // because group changes aren't allowed by the stability manager
+        verifyBuiltList(
+                notif(0),
+                notif(2)
+        );
+    }
+
+    @Test
+    public void testStabilizeGroupsDelayedSummaryRendersAllNotifsTopLevel() {
+        // GIVEN group children posted without a summary
+        addGroupChild(0, PACKAGE_1, GROUP_1);
+        addGroupChild(1, PACKAGE_1, GROUP_1);
+        addGroupChild(2, PACKAGE_1, GROUP_1);
+        addGroupChild(3, PACKAGE_1, GROUP_1);
+
+        dispatchBuild();
+
+        // GIVEN visual stability manager doesn't allow any group changes
+        final TestableStabilityManager stabilityManager =
+                new TestableStabilityManager().setAllowGroupChanges(false);
+        mListBuilder.setNotifStabilityManager(stabilityManager);
+
+        // WHEN the delayed summary is posted
+        addGroupSummary(4, PACKAGE_1, GROUP_1);
+
+        dispatchBuild();
+
+        // THEN all entries are top-level since group changes aren't allowed
+        verifyBuiltList(
+                notif(0),
+                notif(1),
+                notif(2),
+                notif(3),
+                notif(4)
+        );
+
+        // WHEN visual stability manager allows group changes again
+        stabilityManager.setAllowGroupChanges(true);
+        stabilityManager.invalidateList();
+
+        // THEN entries are grouped
+        verifyBuiltList(
+                group(
+                        summary(4),
+                        child(0),
+                        child(1),
+                        child(2),
+                        child(3)
+                )
+        );
+    }
+
+    @Test
+    public void testStabilizeSectionDisallowsNewSection() {
+        // GIVEN one non-default sections
+        final NotifSectioner originalSectioner = new PackageSectioner(PACKAGE_1);
+        mListBuilder.setSectioners(List.of(originalSectioner));
+
+        // GIVEN notifications that's sectioned by sectioner1
+        addNotif(0, PACKAGE_1);
+        dispatchBuild();
+        assertEquals(originalSectioner, mEntrySet.get(0).getSection().getSectioner());
+
+        // WHEN section changes aren't allowed
+        final TestableStabilityManager stabilityManager =
+                new TestableStabilityManager().setAllowSectionChanges(false);
+        mListBuilder.setNotifStabilityManager(stabilityManager);
+
+        // WHEN we try to change the section
+        final NotifSectioner newSectioner = new PackageSectioner(PACKAGE_1);
+        mListBuilder.setSectioners(List.of(newSectioner, originalSectioner));
+        dispatchBuild();
+
+        // THEN the section remains the same since section changes aren't allowed
+        assertEquals(originalSectioner, mEntrySet.get(0).getSection().getSectioner());
+
+        // WHEN section changes are allowed again
+        stabilityManager.setAllowSectionChanges(true);
+        stabilityManager.invalidateList();
+
+        // THEN the section updates
+        assertEquals(newSectioner, mEntrySet.get(0).getSection().getSectioner());
+    }
+
+    @Test
     public void testDispatchListOnBeforeSort() {
         // GIVEN a registered OnBeforeSortListener
         RecordingOnBeforeSortListener listener =
@@ -999,8 +1192,8 @@
     @Test
     public void testDispatchListOnBeforeRender() {
         // GIVEN a registered OnBeforeRenderList
-        RecordingOnBeforeRenderistener listener =
-                new RecordingOnBeforeRenderistener();
+        RecordingOnBeforeRenderListener listener =
+                new RecordingOnBeforeRenderListener();
         mListBuilder.addOnBeforeRenderListListener(listener);
 
         // GIVEN some new notifs out of order
@@ -1450,7 +1643,7 @@
         }
     }
 
-    private static class RecordingOnBeforeRenderistener
+    private static class RecordingOnBeforeRenderListener
             implements OnBeforeRenderListListener {
         List<ListEntry> mEntriesReceived;
 
@@ -1460,6 +1653,39 @@
         }
     }
 
+    private static class TestableStabilityManager extends NotifStabilityManager {
+        boolean mAllowGroupChanges = true;
+        boolean mAllowSectionChanges = true;
+
+        TestableStabilityManager() {
+            super("Test");
+        }
+
+        TestableStabilityManager setAllowGroupChanges(boolean allowGroupChanges) {
+            mAllowGroupChanges = allowGroupChanges;
+            return this;
+        }
+
+        TestableStabilityManager setAllowSectionChanges(boolean allowSectionChanges) {
+            mAllowSectionChanges = allowSectionChanges;
+            return this;
+        }
+
+        @Override
+        public void onBeginRun() {
+        }
+
+        @Override
+        public boolean isGroupChangeAllowed(NotificationEntry entry) {
+            return mAllowGroupChanges;
+        }
+
+        @Override
+        public boolean isSectionChangeAllowed(NotificationEntry entry) {
+            return mAllowSectionChanges;
+        }
+    }
+
     private static final String PACKAGE_1 = "com.test1";
     private static final String PACKAGE_2 = "com.test2";
     private static final String PACKAGE_3 = "org.test3";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 09c9bcd..711f0ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
 import org.junit.Assert.assertFalse
@@ -47,12 +48,10 @@
     private lateinit var promoter: NotifPromoter
     private lateinit var peopleSectioner: NotifSectioner
 
-    @Mock
-    private lateinit var pipeline: NotifPipeline
-    @Mock
-    private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
-    @Mock
-    private lateinit var channel: NotificationChannel
+    @Mock private lateinit var pipeline: NotifPipeline
+    @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+    @Mock private lateinit var channel: NotificationChannel
+    @Mock private lateinit var headerController: NodeController
     private lateinit var entry: NotificationEntry
 
     private lateinit var coordinator: ConversationCoordinator
@@ -60,7 +59,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        coordinator = ConversationCoordinator(peopleNotificationIdentifier)
+        coordinator = ConversationCoordinator(peopleNotificationIdentifier, headerController)
         whenever(channel.isImportantConversation).thenReturn(true)
 
         coordinator.attach(pipeline)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
index fa992a5..7e771ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
@@ -73,6 +74,7 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+    @Mock private NodeController mHeaderController;
 
     private NotificationEntry mEntry;
 
@@ -85,8 +87,8 @@
                 mHeadsUpManager,
                 mHeadsUpViewBinder,
                 mNotificationInterruptStateProvider,
-                mRemoteInputManager
-        );
+                mRemoteInputManager,
+                mHeaderController);
 
         mCoordinator.attach(mNotifPipeline);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 3a7d28a..1031d6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +55,8 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private HighPriorityProvider mHighPriorityProvider;
     @Mock private NotifPipeline mNotifPipeline;
+    @Mock private NodeController mAlertingHeaderController;
+    @Mock private NodeController mSilentHeaderController;
 
     @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
 
@@ -67,8 +70,9 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        RankingCoordinator rankingCoordinator =
-                new RankingCoordinator(mStatusBarStateController, mHighPriorityProvider);
+        RankingCoordinator rankingCoordinator = new RankingCoordinator(
+                mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
+                mSilentHeaderController);
         mEntry = new NotificationEntryBuilder().build();
 
         rankingCoordinator.attach(mNotifPipeline);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 605b4d1..4edca7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -296,6 +296,22 @@
     }
 
     @Test
+    public void testNotSuppressingGroupChangesAnymore_invalidationCalled() {
+        // GIVEN visual stability is being maintained b/c panel is expanded
+        setPulsing(false);
+        setScreenOn(true);
+        setPanelExpanded(true);
+
+        assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+
+        // WHEN the panel isn't expanded anymore
+        setPanelExpanded(false);
+
+        //  invalidate is called because we were previously suppressing a group change
+        verifyInvalidateCalled(true);
+    }
+
+    @Test
     public void testHeadsUp_allowedToChangeGroupAndSection() {
         // GIVEN group + section changes disallowed
         setScreenOn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 7ca2478..8cd7103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -63,6 +64,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -87,6 +89,10 @@
     @Mock private NotificationRowComponent mNotificationRowComponent;
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
     @Mock private NotificationSectionsLogger mLogger;
+    @Mock private SectionHeaderController mIncomingHeaderController;
+    @Mock private SectionHeaderController mPeopleHeaderController;
+    @Mock private SectionHeaderController mAlertingHeaderController;
+    @Mock private SectionHeaderController mSilentHeaderController;
 
     private NotificationSectionsManager mSectionsManager;
 
@@ -109,15 +115,21 @@
                 });
         when(mNotificationRowComponent.getActivatableNotificationViewController())
                 .thenReturn(mActivatableNotificationViewController);
+        when(mIncomingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
+        when(mPeopleHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
+        when(mAlertingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
+        when(mSilentHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
         mSectionsManager =
                 new NotificationSectionsManager(
-                        mActivityStarterDelegate,
                         mStatusBarStateController,
                         mConfigurationController,
-                        mPeopleHubAdapter,
                         mKeyguardMediaController,
                         mSectionsFeatureManager,
-                        mLogger
+                        mLogger,
+                        mIncomingHeaderController,
+                        mPeopleHeaderController,
+                        mAlertingHeaderController,
+                        mSilentHeaderController
                 );
         // Required in order for the header inflation to work properly
         when(mNssl.generateLayoutParams(any(AttributeSet.class)))
@@ -241,8 +253,9 @@
         mSectionsManager.updateSectionBoundaries();
         clearInvocations(mNssl);
 
+        SectionHeaderView silentHeaderView = mSectionsManager.getSilentHeaderView();
         ViewGroup transientParent = mock(ViewGroup.class);
-        mSectionsManager.getSilentHeaderView().setTransientContainer(transientParent);
+        when(silentHeaderView.getTransientContainer()).thenReturn(transientParent);
 
         // WHEN the LO section reappears
         setStackState(
@@ -252,8 +265,8 @@
 
         // THEN the header is first removed from the transient parent before being added to the
         // NSSL.
-        verify(transientParent).removeTransientView(mSectionsManager.getSilentHeaderView());
-        verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 1);
+        verify(transientParent).removeTransientView(silentHeaderView);
+        verify(mNssl).addView(silentHeaderView, 1);
     }
 
     @Test
@@ -358,23 +371,6 @@
     }
 
     @Test
-    public void testPeopleFiltering_keepPeopleHeaderWhenSectionEmpty() {
-        mSectionsManager.setPeopleHubVisible(true);
-        enablePeopleFiltering();
-
-        setStackState(
-                PEOPLE_HEADER,
-                ALERTING_HEADER,
-                ALERTING,
-                GENTLE_HEADER,
-                GENTLE);
-        mSectionsManager.updateSectionBoundaries();
-
-        verify(mNssl, never()).removeView(mSectionsManager.getPeopleHeaderView());
-        verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
-    }
-
-    @Test
     public void testPeopleFiltering_AlertingHunWhilePeopleVisible() {
         enablePeopleFiltering();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index de59ac31..81cbef7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -16,6 +16,8 @@
 import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED;
 import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
 
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 
@@ -43,44 +45,26 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.util.leak.LeakDetector;
 
 import org.junit.After;
 import org.junit.Before;
@@ -92,9 +76,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Tests for {@link NotificationStackScrollLayout}.
  */
@@ -109,7 +90,6 @@
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
     @Mock private StatusBar mBar;
     @Mock private SysuiStatusBarStateController mBarState;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
     @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
     @Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
     @Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
@@ -122,11 +102,9 @@
     @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
     @Mock private NotificationSectionsManager mNotificationSectionsManager;
     @Mock private NotificationSection mNotificationSection;
-    @Mock private FeatureFlags mFeatureFlags;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
     @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
-    @Mock NotificationStackScrollLayoutController mStackScrollLayoutController;
-    private NotificationEntryManager mEntryManager;
+    @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
     private int mOriginalInterruptionModelSetting;
     private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
 
@@ -154,31 +132,6 @@
                 mRemoteInputManager);
         mDependency.injectMockDependency(ShadeController.class);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
-
-        mEntryManager = new NotificationEntryManager(
-                mock(NotificationEntryManagerLogger.class),
-                mock(NotificationGroupManagerLegacy.class),
-                new NotificationRankingManager(
-                        () -> mock(NotificationMediaManager.class),
-                        mGroupMembershipManger,
-                        mHeadsUpManager,
-                        mock(NotificationFilter.class),
-                        mock(NotificationEntryManagerLogger.class),
-                        mock(NotificationSectionsFeatureManager.class),
-                        mock(PeopleNotificationIdentifier.class),
-                        mock(HighPriorityProvider.class)
-                ),
-                mock(NotificationEntryManager.KeyguardEnvironment.class),
-                mock(FeatureFlags.class),
-                () -> mock(NotificationRowBinder.class),
-                () -> mRemoteInputManager,
-                mock(LeakDetector.class),
-                mock(ForegroundServiceDismissalFeatureController.class),
-                mock(IStatusBarService.class)
-        );
-        mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
-        when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
-
         NotificationShelfController notificationShelfController =
                 mock(NotificationShelfController.class);
         NotificationShelf notificationShelf = mock(NotificationShelf.class);
@@ -196,18 +149,13 @@
                 getContext(),
                 null,
                 mNotificationRoundnessManager,
-                mock(DynamicPrivacyController.class),
                 mStatusBarStateController,
                 mNotificationSectionsManager,
                 mock(ForegroundServiceSectionController.class),
                 mock(ForegroundServiceDismissalFeatureController.class),
-                mFeatureFlags,
-                mock(NotifPipeline.class),
-                mEntryManager,
-                mock(NotifCollection.class),
-                mUiEventLoggerFake,
                 mGroupMembershipManger,
-                mGroupExpansionManager
+                mGroupExpansionManager,
+                mUiEventLoggerFake
         );
         mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
                 mNotificationSwipeHelper);
@@ -216,9 +164,9 @@
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
         when(mStackScrollLayoutController.getNoticationRoundessManager())
-                .thenReturn(mock(NotificationRoundnessManager.class));
+                .thenReturn(mNotificationRoundnessManager);
         mStackScroller.setController(mStackScrollLayoutController);
-
+        
         // Stub out functionality that isn't necessary to test.
         doNothing().when(mBar)
                 .executeRunnableDismissingKeyguard(any(Runnable.class),
@@ -323,8 +271,6 @@
     @Test
     public void testUpdateFooter_noNotifications() {
         setBarStateForTest(StatusBarState.SHADE);
-        assertEquals(0, mEntryManager.getActiveNotificationsCount());
-
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
         mStackScroller.updateFooter();
@@ -334,15 +280,15 @@
     @Test
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<NotificationEntry> entries = new ArrayList<>();
-        entries.add(new NotificationEntryBuilder().build());
-        addEntriesToEntryManager(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         when(row.canViewBeDismissed()).thenReturn(true);
         when(mStackScroller.getChildCount()).thenReturn(1);
         when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
         when(mRemoteInputController.isRemoteInputActive()).thenReturn(true);
+        when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+                .thenReturn(true);
+        when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
 
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -354,14 +300,9 @@
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
 
-        ArrayList<NotificationEntry> entries = new ArrayList<>();
-        entries.add(new NotificationEntryBuilder().build());
-        addEntriesToEntryManager(entries);
-
-        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        when(row.canViewBeDismissed()).thenReturn(true);
-        when(mStackScroller.getChildCount()).thenReturn(1);
-        when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
+        when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+                .thenReturn(true);
+        when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
 
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -373,9 +314,13 @@
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
 
-        ArrayList<NotificationEntry> entries = new ArrayList<>();
-        entries.add(new NotificationEntryBuilder().build());
-        addEntriesToEntryManager(entries);
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        when(row.canViewBeDismissed()).thenReturn(false);
+        when(mStackScroller.getChildCount()).thenReturn(1);
+        when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
+        when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
+        when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+                .thenReturn(false);
 
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
@@ -435,7 +380,7 @@
 
     @Test
     public void testClearNotifications_All() {
-        mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true);
+        mStackScroller.clearNotifications(ROWS_ALL, true);
         assertEquals(1, mUiEventLoggerFake.numLogs());
         assertEquals(NotificationStackScrollLayout.NotificationPanelEvent
                 .DISMISS_ALL_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0));
@@ -508,10 +453,4 @@
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
         mStackScroller.setStatusBarState(state);
     }
-
-    private void addEntriesToEntryManager(List<NotificationEntry> entries) {
-        for (NotificationEntry e : entries) {
-            mEntryManager.addActiveNotificationForTest(e);
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 32c6828..60d6c53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.media.KeyguardMediaController;
@@ -44,11 +45,16 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -76,53 +82,38 @@
 @RunWith(AndroidTestingRunner.class)
 public class NotificationStackScrollerControllerTest extends SysuiTestCase {
 
-    @Mock
-    private NotificationGutsManager mNotificationGutsManager;
-    @Mock
-    private HeadsUpManagerPhone mHeadsUpManager;
-    @Mock
-    private NotificationRoundnessManager mNotificationRoundnessManager;
-    @Mock
-    private TunerService mTunerService;
-    @Mock
-    private DynamicPrivacyController mDynamicPrivacyController;
-    @Mock
-    private ConfigurationController mConfigurationController;
-    @Mock
-    private NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock
-    private ZenModeController mZenModeController;
-    @Mock
-    private KeyguardMediaController mKeyguardMediaController;
-    @Mock
-    private SysuiStatusBarStateController mSysuiStatusBarStateController;
-    @Mock
-    private KeyguardBypassController mKeyguardBypassController;
-    @Mock
-    private SysuiColorExtractor mColorExtractor;
-    @Mock
-    private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
-    @Mock
-    private MetricsLogger mMetricsLogger;
-    @Mock
-    private FalsingManager mFalsingManager;
-    @Mock
-    private NotificationSectionsManager mNotificationSectionsManager;
-    @Mock
-    private Resources mResources;
+    @Mock private NotificationGutsManager mNotificationGutsManager;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
+    @Mock private TunerService mTunerService;
+    @Mock private DynamicPrivacyController mDynamicPrivacyController;
+    @Mock private ConfigurationController mConfigurationController;
+    @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
+    @Mock private ZenModeController mZenModeController;
+    @Mock private KeyguardMediaController mKeyguardMediaController;
+    @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
+    @Mock private KeyguardBypassController mKeyguardBypassController;
+    @Mock private SysuiColorExtractor mColorExtractor;
+    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private FalsingManager mFalsingManager;
+    @Mock private NotificationSectionsManager mNotificationSectionsManager;
+    @Mock private Resources mResources;
     @Mock(answer = Answers.RETURNS_SELF)
     private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
-    @Mock
-    private NotificationSwipeHelper mNotificationSwipeHelper;
-    @Mock
-    private StatusBar mStatusBar;
-    @Mock
-    private ScrimController mScrimController;
-    @Mock
-    private NotificationGroupManagerLegacy mLegacyGroupManager;
+    @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
+    @Mock private StatusBar mStatusBar;
+    @Mock private ScrimController mScrimController;
+    @Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
+    @Mock private SectionHeaderController mSilentHeaderController;
+    @Mock private FeatureFlags mFeatureFlags;
+    @Mock private NotifPipeline mNotifPipeline;
+    @Mock private NotifCollection mNotifCollection;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private IStatusBarService mIStatusBarService;
 
     @Captor
-    ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
+    private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
 
     private NotificationStackScrollLayoutController mController;
 
@@ -131,6 +122,7 @@
         MockitoAnnotations.initMocks(this);
 
         when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
+        when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
 
         mController = new NotificationStackScrollLayoutController(
                 true,
@@ -154,7 +146,13 @@
                 mStatusBar,
                 mScrimController,
                 mLegacyGroupManager,
-                mLegacyGroupManager
+                mLegacyGroupManager,
+                mSilentHeaderController,
+                mFeatureFlags,
+                mNotifPipeline,
+                mNotifCollection,
+                mEntryManager,
+                mIStatusBarService
         );
 
         when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
@@ -254,7 +252,6 @@
 
         UserChangedListener changedListener = userChangedCaptor.getValue();
         changedListener.onUserChanged(0);
-        verify(mNotificationStackScrollLayout).setCurrentUserid(0);
         verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 0a041e4..1b05ad7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -38,16 +38,16 @@
 import android.testing.TestableLooper;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardHostView;
+import com.android.keyguard.KeyguardHostViewController;
+import com.android.keyguard.KeyguardRootViewController;
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -76,13 +76,9 @@
     @Mock
     private ViewMediatorCallback mViewMediatorCallback;
     @Mock
-    private LockPatternUtils mLockPatternUtils;
-    @Mock
     private DismissCallbackRegistry mDismissCallbackRegistry;
     @Mock
-    private KeyguardHostView mKeyguardHostView;
-    @Mock
-    private ViewTreeObserver mViewTreeObserver;
+    private KeyguardHostViewController mKeyguardHostViewController;
     @Mock
     private KeyguardBouncer.BouncerExpansionCallback mExpansionCallback;
     @Mock
@@ -96,7 +92,13 @@
     @Mock
     private KeyguardSecurityModel mKeyguardSecurityModel;
     @Mock
+    private KeyguardRootViewController mRootViewController;
+    @Mock
     private ViewGroup mRootView;
+    @Mock
+    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+    @Mock
+    private KeyguardBouncerComponent mKeyguardBouncerComponent;
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
     private Integer mRootVisibility = View.INVISIBLE;
@@ -106,7 +108,6 @@
     public void setup() {
         allowTestableLooperAsMainThread();
         mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
-        mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
         mDependency.injectMockDependency(KeyguardStateController.class);
         when(mRootView.getVisibility()).thenAnswer((Answer<Integer>) invocation -> mRootVisibility);
         doAnswer(invocation -> {
@@ -116,19 +117,22 @@
         when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
                 .thenReturn(KeyguardSecurityModel.SecurityMode.None);
         DejankUtils.setImmediate(true);
+        when(mKeyguardBouncerComponentFactory.create()).thenReturn(mKeyguardBouncerComponent);
+        when(mKeyguardBouncerComponent.getKeyguardHostViewController())
+                .thenReturn(mKeyguardHostViewController);
+        when(mKeyguardBouncerComponent.getKeyguardRootViewController())
+                .thenReturn(mRootViewController);
+
+        when(mRootViewController.getView()).thenReturn(mRootView);
+        when(mRootView.getResources()).thenReturn(mContext.getResources());
+
         final ViewGroup container = new FrameLayout(getContext());
-        when(mKeyguardHostView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
-        when(mKeyguardHostView.getHeight()).thenReturn(500);
-        mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
-                mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
-                mExpansionCallback, mKeyguardStateController, mKeyguardUpdateMonitor,
-                mKeyguardBypassController, mHandler) {
-            @Override
-            protected void inflateView() {
-                mKeyguardView = mKeyguardHostView;
-                mRoot = mRootView;
-            }
-        };
+        mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback,
+                mDismissCallbackRegistry, mFalsingManager,
+                mKeyguardStateController, mKeyguardUpdateMonitor,
+                mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
+                mKeyguardBouncerComponentFactory)
+                .create(container, mExpansionCallback);
     }
 
     @Test
@@ -154,12 +158,10 @@
         mBouncer.ensureView();
         mBouncer.setExpansion(1);
 
-        reset(mKeyguardHostView);
-        when(mKeyguardHostView.getHeight()).thenReturn(500);
+        reset(mKeyguardHostViewController);
 
         mBouncer.show(true);
-        verify(mKeyguardHostView).setAlpha(eq(1f));
-        verify(mKeyguardHostView).setTranslationY(eq(0f));
+        verify(mKeyguardHostViewController).setExpansion(0);
     }
 
     @Test
@@ -177,23 +179,23 @@
     @Test
     public void testShow_triesToDismissKeyguard() {
         mBouncer.show(true);
-        verify(mKeyguardHostView).dismiss(anyInt());
+        verify(mKeyguardHostViewController).dismiss(anyInt());
     }
 
     @Test
     public void testShow_resetsSecuritySelection() {
         mBouncer.show(false);
-        verify(mKeyguardHostView, never()).showPrimarySecurityScreen();
+        verify(mKeyguardHostViewController, never()).showPrimarySecurityScreen();
 
         mBouncer.hide(false);
         mBouncer.show(true);
-        verify(mKeyguardHostView).showPrimarySecurityScreen();
+        verify(mKeyguardHostViewController).showPrimarySecurityScreen();
     }
 
     @Test
     public void testShow_animatesKeyguardView() {
         mBouncer.show(true);
-        verify(mKeyguardHostView).startAppearAnimation();
+        verify(mKeyguardHostViewController).appear(anyInt());
     }
 
     @Test
@@ -201,7 +203,7 @@
         final String errorMessage = "an error message";
         when(mViewMediatorCallback.consumeCustomMessage()).thenReturn(errorMessage);
         mBouncer.show(true);
-        verify(mKeyguardHostView).showErrorMessage(eq(errorMessage));
+        verify(mKeyguardHostViewController).showErrorMessage(eq(errorMessage));
     }
 
     @Test
@@ -218,10 +220,10 @@
         verify(mExpansionCallback).onFullyShown();
 
         verify(mExpansionCallback, never()).onStartingToHide();
-        verify(mKeyguardHostView, never()).onStartingToHide();
+        verify(mKeyguardHostViewController, never()).onStartingToHide();
         mBouncer.setExpansion(0.9f);
         verify(mExpansionCallback).onStartingToHide();
-        verify(mKeyguardHostView).onStartingToHide();
+        verify(mKeyguardHostViewController).onStartingToHide();
     }
 
     @Test
@@ -230,7 +232,7 @@
         mBouncer.setExpansion(0.1f);
 
         mBouncer.setExpansion(0);
-        verify(mKeyguardHostView).onResume();
+        verify(mKeyguardHostViewController).onResume();
         verify(mRootView).announceForAccessibility(any());
     }
 
@@ -267,7 +269,7 @@
     public void testShowPromptReason_propagates() {
         mBouncer.ensureView();
         mBouncer.showPromptReason(1);
-        verify(mKeyguardHostView).showPromptReason(eq(1));
+        verify(mKeyguardHostViewController).showPromptReason(eq(1));
     }
 
     @Test
@@ -275,7 +277,8 @@
         final String message = "a message";
         mBouncer.ensureView();
         mBouncer.showMessage(message, ColorStateList.valueOf(Color.GREEN));
-        verify(mKeyguardHostView).showMessage(eq(message), eq(ColorStateList.valueOf(Color.GREEN)));
+        verify(mKeyguardHostViewController).showMessage(
+                eq(message), eq(ColorStateList.valueOf(Color.GREEN)));
     }
 
     @Test
@@ -283,7 +286,7 @@
         final OnDismissAction dismissAction = () -> false;
         final Runnable cancelAction = () -> {};
         mBouncer.showWithDismissAction(dismissAction, cancelAction);
-        verify(mKeyguardHostView).setOnDismissAction(dismissAction, cancelAction);
+        verify(mKeyguardHostViewController).setOnDismissAction(dismissAction, cancelAction);
         Assert.assertTrue("Should be showing", mBouncer.isShowing());
     }
 
@@ -297,7 +300,7 @@
         ran[0] = false;
         mBouncer.ensureView();
         mBouncer.startPreHideAnimation(r);
-        verify(mKeyguardHostView).startDisappearAnimation(r);
+        verify(mKeyguardHostViewController).startDisappearAnimation(r);
         Assert.assertFalse("Callback should have been deferred", ran[0]);
     }
 
@@ -322,16 +325,14 @@
     public void testSetExpansion() {
         mBouncer.ensureView();
         mBouncer.setExpansion(0.5f);
-        verify(mKeyguardHostView).setAlpha(anyFloat());
-        verify(mKeyguardHostView).setTranslationY(anyFloat());
+        verify(mKeyguardHostViewController).setExpansion(0.5f);
     }
 
     @Test
     public void testIsFullscreenBouncer_asksKeyguardView() {
         mBouncer.ensureView();
         mBouncer.isFullscreenBouncer();
-        verify(mKeyguardHostView).getCurrentSecurityMode();
-        verify(mKeyguardHostView, never()).getSecurityMode();
+        verify(mKeyguardHostViewController).getCurrentSecurityMode();
     }
 
     @Test
@@ -346,21 +347,18 @@
     @Test
     public void testIsHiding_skipsTranslation() {
         mBouncer.show(false /* reset */);
-        reset(mKeyguardHostView);
+        reset(mKeyguardHostViewController);
         mBouncer.startPreHideAnimation(null /* runnable */);
         mBouncer.setExpansion(0.5f);
-        verify(mKeyguardHostView, never()).setTranslationY(anyFloat());
-        verify(mKeyguardHostView, never()).setAlpha(anyFloat());
+        verify(mKeyguardHostViewController, never()).setExpansion(anyFloat());
     }
 
     @Test
     public void testIsSecure() {
-        Assert.assertTrue("Bouncer is secure before inflating views", mBouncer.isSecure());
-
         mBouncer.ensureView();
         for (KeyguardSecurityModel.SecurityMode mode : KeyguardSecurityModel.SecurityMode.values()){
-            reset(mKeyguardHostView);
-            when(mKeyguardHostView.getSecurityMode()).thenReturn(mode);
+            reset(mKeyguardSecurityModel);
+            when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(mode);
             Assert.assertEquals("Security doesn't match for mode: " + mode,
                     mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None);
         }
@@ -392,7 +390,7 @@
     public void testWillDismissWithAction() {
         mBouncer.ensureView();
         Assert.assertFalse("Action not set yet", mBouncer.willDismissWithAction());
-        when(mKeyguardHostView.hasDismissActions()).thenReturn(true);
+        when(mKeyguardHostViewController.hasDismissActions()).thenReturn(true);
         Assert.assertTrue("Action should exist", mBouncer.willDismissWithAction());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1083273..9832d31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -39,7 +39,6 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -92,9 +91,7 @@
     @Mock
     private FaceAuthScreenBrightnessController mFaceAuthScreenBrightnessController;
     @Mock
-    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
-    @Mock
-    private KeyguardBouncerComponent mKeyguardBouncerComponent;
+    private KeyguardBouncer.Factory mKeyguardBouncerFactory;
     @Mock
     private KeyguardBouncer mBouncer;
 
@@ -107,11 +104,10 @@
         when(mLockIconContainer.animate()).thenReturn(mock(ViewPropertyAnimator.class,
                 RETURNS_DEEP_STUBS));
 
-        when(mKeyguardBouncerComponentFactory.build(
+        when(mKeyguardBouncerFactory.create(
                 any(ViewGroup.class),
                 any(KeyguardBouncer.BouncerExpansionCallback.class)))
-                .thenReturn(mKeyguardBouncerComponent);
-        when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer);
+                .thenReturn(mBouncer);
 
         mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
                 getContext(),
@@ -126,7 +122,7 @@
                 mKeyguardStateController,
                 Optional.of(mFaceAuthScreenBrightnessController),
                 mock(NotificationMediaManager.class),
-                mKeyguardBouncerComponentFactory);
+                mKeyguardBouncerFactory);
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
                 mNotificationPanelView, mBiometrucUnlockController,
                 mLockIconContainer, mNotificationContainer, mBypassController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 51cc5f1..94d5458 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -30,15 +30,16 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.onehanded.OneHanded;
-import com.android.systemui.onehanded.OneHandedGestureHandler;
-import com.android.systemui.onehanded.OneHandedTransitionCallback;
 import com.android.systemui.pip.Pip;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.tracing.ProtoTracer;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler;
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
@@ -64,6 +65,7 @@
     @Mock Pip mPip;
     @Mock SplitScreen mSplitScreen;
     @Mock OneHanded mOneHanded;
+    @Mock ShellTaskOrganizer mTaskOrganizer;
     @Mock ProtoTracer mProtoTracer;
 
     @Before
@@ -72,7 +74,7 @@
         mWMShell = new WMShell(mContext, mCommandQueue, mKeyguardUpdateMonitor,
                 mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
                 mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
-                Optional.of(mOneHanded), mProtoTracer);
+                Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
     }
 
     @Test
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_notifications_alert.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..e66d920
--- /dev/null
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M5.85,3.01C3.72,4.82,2.5,7.46,2.5,10.25C2.5,10.66,2.84,11,3.25,11S4,10.66,4,10.25c0-2.35,1.03-4.57,2.82-6.1 C7.14,3.88,7.17,3.41,6.91,3.1C6.64,2.78,6.17,2.74,5.85,3.01z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M21.5,10.25c0-2.79-1.22-5.43-3.35-7.24c-0.32-0.27-0.79-0.23-1.06,0.08c-0.27,0.32-0.23,0.79,0.08,1.06 C18.97,5.68,20,7.9,20,10.25c0,0.41,0.34,0.75,0.75,0.75S21.5,10.66,21.5,10.25z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2.5c-0.83,0-1.5,0.67-1.5,1.5v0.7C7.91,5.36,6,7.71,6,10.5V15c0,0.55-0.45,1-1,1s-1,0.45-1,1v2h16v-2 c0-0.55-0.45-1-1-1s-1-0.45-1-1v-4.5c0-2.79-1.91-5.14-4.5-5.8V4C13.5,3.17,12.83,2.5,12,2.5z M16.5,10.5V15 c0,1.21,0.86,2.22,2,2.45v0.05h-13v-0.05c1.14-0.23,2-1.24,2-2.45v-4.5C7.5,8.02,9.52,6,12,6S16.5,8.02,16.5,10.5z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M14,20h-4c0,1.1,0.9,2,2,2S14,21.1,14,20z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_notifications_alert.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..2f5bdb0e
--- /dev/null
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4.12,9.67C4.42,7.73,5.38,6,6.77,4.73C7.19,4.35,7.2,3.7,6.8,3.3c-0.39-0.39-1-0.39-1.4-0.03 C3.7,4.84,2.52,6.96,2.15,9.34c-0.1,0.61,0.37,1.16,0.99,1.16C3.63,10.5,4.04,10.15,4.12,9.67z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18.6,3.28c-0.4-0.37-1.02-0.36-1.4,0.02c-0.4,0.4-0.38,1.04,0.03,1.42c1.38,1.27,2.35,3,2.65,4.94 c0.08,0.49,0.5,0.84,0.98,0.84c0.61,0,1.09-0.55,0.99-1.16C21.47,6.96,20.29,4.84,18.6,3.28z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,16v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-0.83-0.67-1.5-1.5-1.5S10.5,3.17,10.5,4v0.68C7.63,5.36,6,7.92,6,11v5 l-2.15,2.15c-0.19,0.2-0.19,0.51,0.01,0.71C3.95,18.95,4.07,19,4.2,19h15.6c0.45,0,0.67-0.54,0.35-0.85L18,16z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_notifications_alert.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..c92bdf6
--- /dev/null
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,21 @@
+<!--
+   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.
+-->
+<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+  <path android:fillColor="@android:color/white" android:pathData="M6.81,3.81L5.75,2.75C3.45,4.76,2,7.71,2,11h1.5C3.5,8.13,4.79,5.55,6.81,3.81z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M18.25,2.75l-1.06,1.06C19.21,5.55,20.5,8.13,20.5,11H22C22,7.71,20.55,4.76,18.25,2.75z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M18,10.5c0-4.38-2.72-5.57-4.5-5.89V4c0-1.59-1.43-1.5-1.5-1.5c-0.05,0-1.5-0.09-1.5,1.5v0.62C8.72,4.94,6,6.14,6,10.5v7 H4V19h16v-1.5h-2V10.5z M16.5,17.5h-9v-7C7.5,7.57,8.94,5.95,12,6c3.07-0.05,4.5,1.55,4.5,4.5V17.5z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M12,22c0.07,0,2,0.12,2-2h-4C10,22.12,11.91,22,12,22z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_notifications_alert.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..8f854e7
--- /dev/null
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M14,20h-4c0,1.1,0.9,2,2,2S14,21.1,14,20z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2.5c-0.69,0-1.25,0.56-1.25,1.25v0.77C8.04,5.11,6,7.51,6,10.4V17H4.75C4.34,17,4,17.34,4,17.75s0.34,0.75,0.75,0.75 h14.5c0.41,0,0.75-0.34,0.75-0.75S19.66,17,19.25,17H18v-6.6c0-2.88-2.04-5.29-4.75-5.87V3.75C13.25,3.06,12.69,2.5,12,2.5z M16.5,10.4V17h-9v-6.6c0-2.48,2.02-4.5,4.5-4.5S16.5,7.91,16.5,10.4z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M5.85,3.01C3.72,4.82,2.5,7.46,2.5,10.25C2.5,10.66,2.84,11,3.25,11S4,10.66,4,10.25c0-2.35,1.03-4.57,2.82-6.1 C7.14,3.88,7.17,3.41,6.91,3.1C6.64,2.78,6.17,2.74,5.85,3.01z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18.15,3.01c-0.32-0.27-0.79-0.23-1.06,0.08c-0.27,0.32-0.23,0.79,0.08,1.06C18.97,5.68,20,7.9,20,10.25 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75C21.5,7.46,20.28,4.82,18.15,3.01z" />
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_notifications_alert.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..e022c63
--- /dev/null
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,21 @@
+<!--
+   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.
+-->
+<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+  <path android:fillColor="@android:color/white" android:pathData="M6.47,4.81c0.37-0.38,0.35-0.99-0.03-1.37c-0.4-0.4-1.05-0.39-1.44,0.02c-1.62,1.72-2.7,3.95-2.95,6.43 C2,10.48,2.46,11,3.05,11h0.01c0.51,0,0.93-0.38,0.98-0.88C4.24,8.07,5.13,6.22,6.47,4.81z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M18.99,3.47c-0.39-0.41-1.04-0.42-1.44-0.02c-0.38,0.38-0.39,0.98-0.03,1.37c1.34,1.41,2.23,3.26,2.43,5.3 c0.05,0.5,0.48,0.88,0.98,0.88h0.01c0.59,0,1.05-0.52,0.99-1.11C21.69,7.42,20.61,5.19,18.99,3.47z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M19,17h-1v-6c0-3.07-1.63-5.64-4.5-6.32V4c0-0.83-0.67-1.5-1.5-1.5c-0.83,0-1.5,0.67-1.5,1.5v0.68C7.64,5.36,6,7.92,6,11 v6H5c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1C20,17.45,19.55,17,19,17z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_notifications_alert.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..1e25d27
--- /dev/null
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,21 @@
+<!--
+   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.
+-->
+<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+  <path android:fillColor="@android:color/white" android:pathData="M18,6.5L16.5,5H13V3h-2v2H7.5L6,6.5v11H4V19h16v-1.5h-2V6.5z M7.5,17.5v-11h9v11H7.5z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0,2-0.9,2-2h-4C10,21.1,10.9,22,12,22z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M6.81,3.81L5.75,2.75C3.45,4.76,2,7.71,2,11h1.5C3.5,8.13,4.79,5.55,6.81,3.81z"/>
+  <path android:fillColor="@android:color/white" android:pathData="M18.25,2.75l-1.06,1.06C19.21,5.55,20.5,8.13,20.5,11H22C22,7.71,20.55,4.76,18.25,2.75z"/>
+</vector>
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 3d6a128..4473754 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -72,7 +72,8 @@
             };
 
     /**
-     * Register a UiAutomation. Only one may be registered at a time.
+     * Register a UiAutomation if it uses the accessibility subsystem. Only one may be registered
+     * at a time.
      *
      * @param owner A binder object owned by the process that owns the UiAutomation to be
      *              registered.
@@ -80,6 +81,7 @@
      * @param accessibilityServiceInfo The UiAutomation's service info
      * @param flags The UiAutomation's flags
      * @param id The id for the service connection
+     * @see UiAutomation#FLAG_DONT_USE_ACCESSIBILITY
      */
     void registerUiTestAutomationServiceLocked(IBinder owner,
             IAccessibilityServiceClient serviceClient,
@@ -88,14 +90,15 @@
             AccessibilitySecurityPolicy securityPolicy,
             AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
             WindowManagerInternal windowManagerInternal,
-            SystemActionPerformer systemActionPerfomer,
+            SystemActionPerformer systemActionPerformer,
             AccessibilityWindowManager awm, int flags) {
         synchronized (mLock) {
             accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
 
             if (mUiAutomationService != null) {
-                throw new IllegalStateException("UiAutomationService " + serviceClient
-                        + "already registered!");
+                throw new IllegalStateException(
+                        "UiAutomationService " + mUiAutomationService.mServiceInterface
+                                + "already registered!");
             }
 
             try {
@@ -106,12 +109,17 @@
                 return;
             }
 
+            mUiAutomationFlags = flags;
             mSystemSupport = systemSupport;
+            // Ignore registering UiAutomation if it is not allowed to use the accessibility
+            // subsystem.
+            if (!useAccessibility()) {
+                return;
+            }
             mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
                     mainHandler, mLock, securityPolicy, systemSupport, windowManagerInternal,
-                    systemActionPerfomer, awm);
+                    systemActionPerformer, awm);
             mUiAutomationServiceOwner = owner;
-            mUiAutomationFlags = flags;
             mUiAutomationServiceInfo = accessibilityServiceInfo;
             mUiAutomationService.mServiceInterface = serviceClient;
             mUiAutomationService.onAdded();
@@ -130,15 +138,15 @@
 
     void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
         synchronized (mLock) {
-            if ((mUiAutomationService == null)
+            if (useAccessibility()
+                    && ((mUiAutomationService == null)
                     || (serviceClient == null)
                     || (mUiAutomationService.mServiceInterface == null)
                     || (serviceClient.asBinder()
-                    != mUiAutomationService.mServiceInterface.asBinder())) {
+                    != mUiAutomationService.mServiceInterface.asBinder()))) {
                 throw new IllegalStateException("UiAutomationService " + serviceClient
                         + " not registered!");
             }
-
             destroyUiAutomationService();
         }
     }
@@ -150,14 +158,19 @@
     }
 
     boolean isUiAutomationRunningLocked() {
-        return (mUiAutomationService != null);
+        return (mUiAutomationService != null || !useAccessibility());
     }
 
     boolean suppressingAccessibilityServicesLocked() {
-        return (mUiAutomationService != null) && ((mUiAutomationFlags
+        return (mUiAutomationService != null || !useAccessibility())
+                && ((mUiAutomationFlags
                 & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0);
     }
 
+    boolean useAccessibility() {
+        return ((mUiAutomationFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0);
+    }
+
     boolean isTouchExplorationEnabledLocked() {
         return (mUiAutomationService != null)
                 && mUiAutomationService.mRequestTouchExplorationMode;
@@ -209,14 +222,14 @@
                 mUiAutomationService.onRemoved();
                 mUiAutomationService.resetLocked();
                 mUiAutomationService = null;
-                mUiAutomationFlags = 0;
                 if (mUiAutomationServiceOwner != null) {
                     mUiAutomationServiceOwner.unlinkToDeath(
                             mUiAutomationServiceOwnerDeathRecipient, 0);
                     mUiAutomationServiceOwner = null;
                 }
-                mSystemSupport.onClientChangeLocked(false);
             }
+            mUiAutomationFlags = 0;
+            mSystemSupport.onClientChangeLocked(false);
         }
     }
 
@@ -227,9 +240,9 @@
                 int id, Handler mainHandler, Object lock,
                 AccessibilitySecurityPolicy securityPolicy,
                 SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
-                SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
+                SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm) {
             super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
-                    securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
+                    securityPolicy, systemSupport, windowManagerInternal, systemActionPerformer,
                     awm);
             mMainHandler = mainHandler;
         }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index d8c5f6f..4bd987a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -112,7 +112,7 @@
     }
 
     public String getSettingValue(ContentResolver resolver) {
-        return Settings.Secure.getString(resolver, SETTING);
+        return Settings.Secure.getStringForUser(resolver, SETTING, resolver.getUserId());
     }
 
     public synchronized void update(KeyValueListParser parser) {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 65ac784..d92706d 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -39,6 +39,7 @@
 import android.os.BatteryProperty;
 import android.os.BatteryStats;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.DropBoxManager;
 import android.os.FileUtils;
@@ -59,6 +60,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.battery.BatteryServiceDumpProto;
+import android.sysprop.PowerProperties;
 import android.util.EventLog;
 import android.util.MutableInt;
 import android.util.Slog;
@@ -182,6 +184,7 @@
     private int mChargeStartLevel;
 
     private boolean mUpdatesStopped;
+    private boolean mBatteryInputSuspended;
 
     private Led mLed;
 
@@ -234,6 +237,8 @@
             invalidChargerObserver.startObserving(
                     "DEVPATH=/devices/virtual/switch/invalid_charger");
         }
+
+        mBatteryInputSuspended = PowerProperties.battery_input_suspended().orElse(false);
     }
 
     @Override
@@ -876,6 +881,10 @@
         pw.println("  reset [-f]");
         pw.println("    Unfreeze battery state, returning to current hardware values.");
         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
+        if (Build.IS_DEBUGGABLE) {
+            pw.println("  disable_charge");
+            pw.println("    Suspend charging even if plugged in. ");
+        }
     }
 
     static final int OPTION_FORCE_UPDATE = 1<<0;
@@ -997,6 +1006,20 @@
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
+                if (mBatteryInputSuspended) {
+                    PowerProperties.battery_input_suspended(false);
+                    mBatteryInputSuspended = false;
+                }
+            } break;
+            case "suspend_input": {
+                if (!Build.IS_DEBUGGABLE) {
+                    throw new SecurityException(
+                            "battery suspend_input is only supported on debuggable builds");
+                }
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, null);
+                PowerProperties.battery_input_suspended(true);
+                mBatteryInputSuspended = true;
             } break;
             default:
                 return shell.handleDefaultCommands(cmd);
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index 3bcb36f..4de4075 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -19,6 +19,7 @@
 import android.Manifest.permission;
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.PermissionChecker;
@@ -404,7 +405,8 @@
         }
 
         public int getSecureInt(Context context, String name, int defaultValue) {
-            return Settings.Secure.getInt(context.getContentResolver(), name, defaultValue);
+            final ContentResolver cr = context.getContentResolver();
+            return Settings.Secure.getIntForUser(cr, name, defaultValue, cr.getUserId());
         }
     }
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 27c5d4a..eb18da2 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4687,14 +4687,19 @@
             }
         }
 
-        public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) {
+        public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode,
+                int previousMode) {
             final long token = Binder.clearCallingIdentity();
             try {
                 // When using FUSE, we may need to kill the app if the op changes
                 switch(code) {
                     case OP_REQUEST_INSTALL_PACKAGES:
-                        // Always kill regardless of op change, to remount apps /storage
-                        killAppForOpChange(code, uid);
+                        if (previousMode == MODE_ALLOWED || mode == MODE_ALLOWED) {
+                            // If we transition to/from MODE_ALLOWED, kill the app to make
+                            // sure it has the correct view of /storage. Changing between
+                            // MODE_DEFAULT / MODE_ERRORED is a no-op
+                            killAppForOpChange(code, uid);
+                        }
                         return;
                     case OP_MANAGE_EXTERNAL_STORAGE:
                         if (mode != MODE_ALLOWED) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 343e05d..20f3231 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -37,6 +37,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -116,6 +117,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -135,9 +138,31 @@
 
     private static final boolean SHOW_DUNGEON_NOTIFICATION = false;
 
-    //TODO: remove this when development is done.
-    private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
-    private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+    public static final int FGS_FEATURE_DENIED = 0;
+    public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 1;
+    public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 2;
+    public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 3;
+    public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 4;
+    public static final int FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION = 5;
+    public static final int FGS_FEATURE_ALLOWED_BY_TOKEN = 6;
+    public static final int FGS_FEATURE_ALLOWED_BY_PERMISSION = 7;
+    public static final int FGS_FEATURE_ALLOWED_BY_WHITELIST = 8;
+    public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 9;
+
+    @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = {
+            FGS_FEATURE_DENIED,
+            FGS_FEATURE_ALLOWED_BY_PROC_STATE,
+            FGS_FEATURE_ALLOWED_BY_UID_VISIBLE,
+            FGS_FEATURE_ALLOWED_BY_FLAG,
+            FGS_FEATURE_ALLOWED_BY_SYSTEM_UID,
+            FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_TOKEN,
+            FGS_FEATURE_ALLOWED_BY_PERMISSION,
+            FGS_FEATURE_ALLOWED_BY_WHITELIST,
+            FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FgsFeatureRetCode {}
 
     // How long we wait for a service to finish executing.
     static final int SERVICE_TIMEOUT = 20*1000;
@@ -519,12 +544,13 @@
         }
 
         if (fgRequired) {
-            if (!r.mAllowStartForeground) {
+            if (isFgsBgStart(r.mAllowStartForeground)) {
                 if (!r.mLoggedInfoAllowStartForeground) {
                     Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground);
                     r.mLoggedInfoAllowStartForeground = true;
                 }
-                if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
+                if (r.mAllowStartForeground == FGS_FEATURE_DENIED
+                        && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
                     Slog.w(TAG, "startForegroundService() not allowed due to "
                                     + " mAllowStartForeground false: service "
                                     + r.shortInstanceName);
@@ -768,9 +794,7 @@
         FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid,
                 r.name.getPackageName(), r.name.getClassName(),
                 FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
-        synchronized (r.stats.getBatteryStats()) {
-            r.stats.startRunningLocked();
-        }
+        mAm.mBatteryStatsService.noteServiceStartRunning(r.stats);
         String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
         if (error != null) {
             return new ComponentName("!!", error);
@@ -809,9 +833,7 @@
         FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, service.appInfo.uid,
                 service.name.getPackageName(), service.name.getClassName(),
                 FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
-        synchronized (service.stats.getBatteryStats()) {
-            service.stats.stopRunningLocked();
-        }
+        mAm.mBatteryStatsService.noteServiceStopRunning(service.stats);
         service.startRequested = false;
         if (service.tracker != null) {
             service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -970,9 +992,7 @@
             FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid,
                     r.name.getPackageName(), r.name.getClassName(),
                     FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
-            synchronized (r.stats.getBatteryStats()) {
-                r.stats.stopRunningLocked();
-            }
+            mAm.mBatteryStatsService.noteServiceStopRunning(r.stats);
             r.startRequested = false;
             if (r.tracker != null) {
                 r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -1427,13 +1447,14 @@
                 }
 
                 if (!ignoreForeground) {
-                    if (!r.mAllowStartForeground) {
+                    if (isFgsBgStart(r.mAllowStartForeground)) {
                         if (!r.mLoggedInfoAllowStartForeground) {
                             Slog.wtf(TAG, "Background started FGS "
                                     + r.mInfoAllowStartForeground);
                             r.mLoggedInfoAllowStartForeground = true;
                         }
-                        if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
+                        if (r.mAllowStartForeground == FGS_FEATURE_DENIED
+                                && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
                             Slog.w(TAG,
                                     "Service.startForeground() not allowed due to "
                                             + "mAllowStartForeground false: service "
@@ -2518,7 +2539,8 @@
                     synchronized (stats) {
                         ss = stats.getServiceStatsLocked(
                                 sInfo.applicationInfo.uid, name.getPackageName(),
-                                name.getClassName());
+                                name.getClassName(), SystemClock.elapsedRealtime(),
+                                SystemClock.uptimeMillis());
                     }
                     r = new ServiceRecord(mAm, ss, className, name, definingPackageName,
                             definingUid, filter, sInfo, callingFromFg, res);
@@ -3056,9 +3078,7 @@
             }
             FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_LAUNCH_REPORTED, r.appInfo.uid,
                     r.name.getPackageName(), r.name.getClassName());
-            synchronized (r.stats.getBatteryStats()) {
-                r.stats.startLaunchedLocked();
-            }
+            mAm.mBatteryStatsService.noteServiceStartLaunch(r.stats);
             mAm.notifyPackageUse(r.serviceInfo.packageName,
                                  PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
@@ -3402,9 +3422,7 @@
         smap.mDelayedStartList.remove(r);
 
         if (r.app != null) {
-            synchronized (r.stats.getBatteryStats()) {
-                r.stats.stopLaunchedLocked();
-            }
+            mAm.mBatteryStatsService.noteServiceStopLaunch(r.stats);
             r.app.stopService(r);
             r.app.updateBoundClientUids();
             if (r.whitelistManager) {
@@ -3940,9 +3958,7 @@
         // Clear app state from services.
         for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
             ServiceRecord sr = app.getRunningServiceAt(i);
-            synchronized (sr.stats.getBatteryStats()) {
-                sr.stats.stopLaunchedLocked();
-            }
+            mAm.mBatteryStatsService.noteServiceStopLaunch(sr.stats);
             if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
                 sr.app.stopService(sr);
                 sr.app.updateBoundClientUids();
@@ -4885,13 +4901,13 @@
             r.mAllowWhileInUsePermissionInFgs = true;
         }
 
-        if (!r.mAllowWhileInUsePermissionInFgs || !r.mAllowStartForeground) {
-            final boolean temp = shouldAllowFgsFeatureLocked(callingPackage, callingPid,
-                    callingUid, intent, r, allowBackgroundActivityStarts);
+        if (!r.mAllowWhileInUsePermissionInFgs || (r.mAllowStartForeground == FGS_FEATURE_DENIED)) {
+            final @FgsFeatureRetCode int temp = shouldAllowFgsFeatureLocked(callingPackage,
+                    callingPid, callingUid, intent, r, allowBackgroundActivityStarts);
             if (!r.mAllowWhileInUsePermissionInFgs) {
-                r.mAllowWhileInUsePermissionInFgs = temp;
+                r.mAllowWhileInUsePermissionInFgs = (temp != FGS_FEATURE_DENIED);
             }
-            if (!r.mAllowStartForeground) {
+            if (r.mAllowStartForeground == FGS_FEATURE_DENIED) {
                 r.mAllowStartForeground = temp;
             }
         }
@@ -4903,85 +4919,141 @@
      * @param callingUid caller app's uid.
      * @param intent intent to start/bind service.
      * @param r the service to start.
-     * @return true if allow, false otherwise.
+     * @return {@link FgsFeatureRetCode}
      */
-    private boolean shouldAllowFgsFeatureLocked(String callingPackage,
+    private @FgsFeatureRetCode int shouldAllowFgsFeatureLocked(String callingPackage,
             int callingPid, int callingUid, Intent intent, ServiceRecord r,
             boolean allowBackgroundActivityStarts) {
-        // Is the allow activity background start flag on?
-        if (allowBackgroundActivityStarts) {
-            return true;
-        }
+        int ret = FGS_FEATURE_DENIED;
 
-        boolean isCallerSystem = false;
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        switch (callingAppId) {
-            case ROOT_UID:
-            case SYSTEM_UID:
-            case NFC_UID:
-            case SHELL_UID:
-                isCallerSystem = true;
-                break;
-            default:
-                isCallerSystem = false;
-                break;
-        }
-
-        if (isCallerSystem) {
-            return true;
-        }
-
-        if (r.app != null) {
-            ActiveInstrumentation instr = r.app.getActiveInstrumentation();
-            if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
-                return true;
-            }
-            if (r.app.areBackgroundActivityStartsAllowedByToken()) {
-                return true;
-            }
-        }
-
-        if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
-                == PERMISSION_GRANTED) {
-            return true;
-        }
-
-        // Is the calling UID at PROCESS_STATE_TOP or above?
+        final StringBuilder sb = new StringBuilder(64);
         final int uidState = mAm.getUidState(callingUid);
-        if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
-            return true;
+        if (ret == FGS_FEATURE_DENIED) {
+            // Is the calling UID at PROCESS_STATE_TOP or above?
+            if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
+                sb.append("uidState=").append(uidState);
+                ret = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+            }
         }
 
-        // Does the calling UID have any visible activity?
-        final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
-        if (isCallingUidVisible) {
-            return true;
+        if (ret == FGS_FEATURE_DENIED) {
+            // Does the calling UID have any visible activity?
+            final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
+            if (isCallingUidVisible) {
+                ret = FGS_FEATURE_ALLOWED_BY_UID_VISIBLE;
+            }
         }
 
-        final boolean isWhiteListedPackage =
-                mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
-        if (isWhiteListedPackage) {
-            return true;
+        if (ret == FGS_FEATURE_DENIED) {
+            // Is the allow activity background start flag on?
+            if (allowBackgroundActivityStarts) {
+                ret = FGS_FEATURE_ALLOWED_BY_FLAG;
+            }
         }
 
-        // Is the calling UID a device owner app?
-        final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
-        if (isDeviceOwner) {
-            return true;
+        if (ret == FGS_FEATURE_DENIED) {
+            boolean isCallerSystem = false;
+            final int callingAppId = UserHandle.getAppId(callingUid);
+            switch (callingAppId) {
+                case ROOT_UID:
+                case SYSTEM_UID:
+                case NFC_UID:
+                case SHELL_UID:
+                    isCallerSystem = true;
+                    break;
+                default:
+                    isCallerSystem = false;
+                    break;
+            }
+
+            if (isCallerSystem) {
+                sb.append("callingUid=").append(callingAppId);
+                ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+            }
         }
 
-        final String info =
+        if (ret == FGS_FEATURE_DENIED) {
+            if (r.app != null) {
+                ActiveInstrumentation instr = r.app.getActiveInstrumentation();
+                if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
+                    ret = FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION;
+                }
+                if (r.app.areBackgroundActivityStartsAllowedByToken()) {
+                    ret = FGS_FEATURE_ALLOWED_BY_TOKEN;
+                }
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+                    == PERMISSION_GRANTED) {
+                ret = FGS_FEATURE_ALLOWED_BY_PERMISSION;
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            final boolean isWhiteListedPackage =
+                    mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
+            if (isWhiteListedPackage) {
+                ret = FGS_FEATURE_ALLOWED_BY_WHITELIST;
+            }
+        }
+
+        if (ret == FGS_FEATURE_DENIED) {
+            // Is the calling UID a device owner app?
+            final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
+            if (isDeviceOwner) {
+                ret = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+            }
+        }
+
+        final String debugInfo =
                 "[callingPackage: " + callingPackage
                         + "; callingUid: " + callingUid
                         + "; uidState: " + ProcessList.makeProcStateString(uidState)
                         + "; intent: " + intent
+                        + "; code:" + fgsCodeToString(ret)
+                        + "; extra:" + sb.toString()
                         + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
                         + "]";
-        if (!info.equals(r.mInfoAllowStartForeground)) {
+        if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
             r.mLoggedInfoAllowStartForeground = false;
-            r.mInfoAllowStartForeground = info;
+            r.mInfoAllowStartForeground = debugInfo;
         }
 
-        return false;
+        return ret;
     }
+
+    private static String fgsCodeToString(@FgsFeatureRetCode int code) {
+        switch (code) {
+            case FGS_FEATURE_DENIED:
+                return "DENIED";
+            case FGS_FEATURE_ALLOWED_BY_PROC_STATE:
+                return "ALLOWED_BY_PROC_STATE";
+            case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE:
+                return "ALLOWED_BY_UID_VISIBLE";
+            case FGS_FEATURE_ALLOWED_BY_FLAG:
+                return "ALLOWED_BY_FLAG";
+            case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID:
+                return "ALLOWED_BY_SYSTEM_UID";
+            case FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION:
+                return "ALLOWED_BY_INSTR_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_TOKEN:
+                return "ALLOWED_BY_TOKEN";
+            case FGS_FEATURE_ALLOWED_BY_PERMISSION:
+                return "ALLOWED_BY_PERMISSION";
+            case FGS_FEATURE_ALLOWED_BY_WHITELIST:
+                return "ALLOWED_BY_WHITELIST";
+            case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER:
+                return "ALLOWED_BY_DEVICE_OWNER";
+            default:
+                return "";
+        }
+    }
+
+    private static boolean isFgsBgStart(@FgsFeatureRetCode int code) {
+        return code != FGS_FEATURE_ALLOWED_BY_PROC_STATE
+                && code != FGS_FEATURE_ALLOWED_BY_UID_VISIBLE;
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a5d2011..b55d555 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -97,7 +97,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
@@ -110,7 +109,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -133,18 +131,16 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
 
-
 import android.Manifest;
 import android.Manifest.permission;
-import android.annotation.BroadcastBehavior;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -266,7 +262,6 @@
 import android.os.storage.StorageManager;
 import android.permission.PermissionManagerInternal.CheckPermissionDelegate;
 import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.server.ServerProtoEnums;
 import android.sysprop.InitProperties;
@@ -342,7 +337,6 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.SystemServiceManager;
 import com.android.server.ThreadPriorityBooster;
 import com.android.server.UserspaceRebootLogger;
@@ -394,11 +388,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
 
 public class ActivityManagerService extends IActivityManager.Stub
@@ -424,7 +414,6 @@
     private static final String TAG_POWER = TAG + POSTFIX_POWER;
     static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
     static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
-    static final String TAG_PSS = TAG + POSTFIX_PSS;
     private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
     static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
@@ -438,21 +427,9 @@
             "com.android.internal.intent.action.BUGREPORT_REQUESTED";
     private static final String SHELL_APP_PACKAGE = "com.android.shell";
 
-    /** Control over CPU and battery monitoring */
-    // write battery stats every 30 minutes.
-    static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
-    static final boolean MONITOR_CPU_USAGE = true;
-    // don't sample cpu less than every 5 seconds.
-    static final long MONITOR_CPU_MIN_TIME = 5 * 1000;
-    // wait possibly forever for next cpu sample.
-    static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;
-    static final boolean MONITOR_THREAD_CPU_USAGE = false;
-
     // The flags that are set for all calls we make to the package manager.
     public static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
 
-    static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
-
     static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
 
     public static final String ANR_TRACE_DIR = "/data/anr";
@@ -495,37 +472,6 @@
     private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
             "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
 
-    /**
-     * Broadcast sent when heap dump collection has been completed.
-     */
-    @BroadcastBehavior(includeBackground = true, protectedBroadcast = true)
-    private static final String ACTION_HEAP_DUMP_FINISHED =
-            "com.android.internal.intent.action.HEAP_DUMP_FINISHED";
-
-    /**
-     * The process we are reporting
-     */
-    private static final String EXTRA_HEAP_DUMP_PROCESS_NAME =
-            "com.android.internal.extra.heap_dump.PROCESS_NAME";
-
-    /**
-     * The size limit the process reached.
-     */
-    private static final String EXTRA_HEAP_DUMP_SIZE_BYTES =
-            "com.android.internal.extra.heap_dump.SIZE_BYTES";
-
-    /**
-     * Whether the user initiated the dump or not.
-     */
-    private static final String EXTRA_HEAP_DUMP_IS_USER_INITIATED =
-            "com.android.internal.extra.heap_dump.IS_USER_INITIATED";
-
-    /**
-     * Optional name of package to directly launch.
-     */
-    private static final String EXTRA_HEAP_DUMP_REPORT_PACKAGE =
-            "com.android.internal.extra.heap_dump.REPORT_PACKAGE";
-
     // If set, we will push process association information in to procstats.
     static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
 
@@ -558,7 +504,6 @@
     private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
 
     OomAdjuster mOomAdjuster;
-    final LowMemDetector mLowMemDetector;
 
     static final String EXTRA_TITLE = "android.intent.extra.TITLE";
     static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
@@ -920,28 +865,11 @@
      */
     final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
 
-    /**
-     * Processes we want to collect PSS data from.
-     */
-    final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
-
-    /**
-     * Depth of overlapping activity-start PSS deferral notes
-     */
-    private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
-
     private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
             new ActivityMetricsLaunchObserver() {
         @Override
         public void onActivityLaunched(byte[] activity, int temperature) {
-            // This is safe to force to the head of the queue because it relies only
-            // on refcounting to track begin/end of deferrals, not on actual
-            // message ordering.  We don't care *what* activity is being
-            // launched; only that we're doing so.
-            if (mPssDeferralTime > 0) {
-                final Message msg = mBgHandler.obtainMessage(DEFER_PSS_MSG);
-                mBgHandler.sendMessageAtFrontOfQueue(msg);
-            }
+            mAppProfiler.onActivityLaunched();
         }
 
         // The other observer methods are unused
@@ -966,45 +894,9 @@
         }
     };
 
-    /**
-     * How long we defer PSS gathering while activities are starting, in milliseconds.
-     * This is adjustable via DeviceConfig.  If it is zero or negative, no PSS deferral
-     * is done.
-     */
-    private volatile long mPssDeferralTime = 0;
-    private static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
-
     private boolean mBinderTransactionTrackingEnabled = false;
 
     /**
-     * Last time we requested PSS data of all processes.
-     */
-    long mLastFullPssTime = SystemClock.uptimeMillis();
-
-    /**
-     * If set, the next time we collect PSS data we should do a full collection
-     * with data from native processes and the kernel.
-     */
-    boolean mFullPssPending = false;
-
-    /**
-     * Observe DeviceConfig changes to the PSS calculation interval
-     */
-    private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
-            new DeviceConfig.OnPropertiesChangedListener() {
-                @Override
-                public void onPropertiesChanged(Properties properties) {
-                    if (properties.getKeyset().contains(ACTIVITY_START_PSS_DEFER_CONFIG)) {
-                        mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
-                        if (DEBUG_PSS) {
-                            Slog.d(TAG_PSS, "Activity-start PSS delay now "
-                                    + mPssDeferralTime + " ms");
-                        }
-                    }
-                }
-            };
-
-    /**
      * Fingerprints (hashCode()) of stack traces that we've
      * already logged DropBox entries for.  Guarded by itself.  If
      * something (rogue user app) forces this over
@@ -1244,41 +1136,11 @@
     int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
 
     /**
-     * Allow the current computed overall memory level of the system to go down?
-     * This is set to false when we are killing processes for reasons other than
-     * memory management, so that the now smaller process list will not be taken as
-     * an indication that memory is tighter.
-     */
-    boolean mAllowLowerMemLevel = false;
-
-    /**
-     * The last computed memory level, for holding when we are in a state that
-     * processes are going away for other reasons.
-     */
-    int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
-
-    /**
-     * The last total number of process we have, to determine if changes actually look
-     * like a shrinking number of process due to lower RAM.
-     */
-    int mLastNumProcesses;
-
-    /**
      * The uptime of the last time we performed idle maintenance.
      */
     long mLastIdleTime = SystemClock.uptimeMillis();
 
     /**
-     * Total time spent with RAM that has been added in the past since the last idle time.
-     */
-    long mLowRamTimeSinceLastIdle = 0;
-
-    /**
-     * If RAM is currently low, when that horrible situation started.
-     */
-    long mLowRamStartTime = 0;
-
-    /**
      * For reporting to battery stats the current top application.
      */
     private String mCurResumedPackage = null;
@@ -1297,13 +1159,6 @@
      */
     boolean mSafeMode;
 
-    /**
-     * If true, we are running under a test environment so will sample PSS from processes
-     * much more rapidly to try to collect better data when the tests are rapidly
-     * running through apps.
-     */
-    boolean mTestPssMode = false;
-
     String mDebugApp = null;
     boolean mWaitForDebugger = false;
     boolean mDebugTransient = false;
@@ -1311,61 +1166,6 @@
     boolean mOrigWaitForDebugger = false;
     boolean mAlwaysFinishActivities = false;
 
-    class ProfileData {
-        private String mProfileApp = null;
-        private ProcessRecord mProfileProc = null;
-        private ProfilerInfo mProfilerInfo = null;
-
-        void setProfileApp(String profileApp) {
-            mProfileApp = profileApp;
-            if (mAtmInternal != null) {
-                mAtmInternal.setProfileApp(profileApp);
-            }
-        }
-
-        String getProfileApp() {
-            return mProfileApp;
-        }
-
-        void setProfileProc(ProcessRecord profileProc) {
-            mProfileProc = profileProc;
-            if (mAtmInternal != null) {
-                mAtmInternal.setProfileProc(profileProc == null ? null
-                        : profileProc.getWindowProcessController());
-            }
-        }
-
-        ProcessRecord getProfileProc() {
-            return mProfileProc;
-        }
-
-        void setProfilerInfo(ProfilerInfo profilerInfo) {
-            mProfilerInfo = profilerInfo;
-            if (mAtmInternal != null) {
-                mAtmInternal.setProfilerInfo(profilerInfo);
-            }
-        }
-
-        ProfilerInfo getProfilerInfo() {
-            return mProfilerInfo;
-        }
-    }
-    final ProfileData mProfileData = new ProfileData();
-
-    /**
-     * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
-     * is not null, this map is checked and the mapped agent installed during bind-time. Note:
-     * A non-null agent in mProfileInfo overrides this.
-     */
-    private @Nullable Map<String, String> mAppAgentMap = null;
-
-    int mProfileType = 0;
-    final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
-    String mMemWatchDumpProcName;
-    Uri mMemWatchDumpUri;
-    int mMemWatchDumpPid;
-    int mMemWatchDumpUid;
-    private boolean mMemWatchIsUserInitiated;
     String mTrackAllocationApp = null;
     String mNativeDebuggingApp = null;
 
@@ -1404,27 +1204,6 @@
 
     final AnrHelper mAnrHelper = new AnrHelper(this);
 
-    /**
-     * Runtime CPU use collection thread.  This object's lock is used to
-     * perform synchronization with the thread (notifying it to run).
-     */
-    final Thread mProcessCpuThread;
-
-    /**
-     * Used to collect per-process CPU use for ANRs, battery stats, etc.
-     * Must acquire this object's lock when accessing it.
-     * NOTE: this lock will be held while doing long operations (trawling
-     * through all processes in /proc), so it should never be acquired by
-     * any critical paths such as when holding the main activity manager lock.
-     */
-    final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
-            MONITOR_THREAD_CPU_USAGE);
-    final AtomicLong mLastCpuTime = new AtomicLong(0);
-    final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
-    final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
-
-    long mLastWriteTime = 0;
-
     /** Set to true after the system has finished booting. */
     volatile boolean mBooted = false;
 
@@ -1563,6 +1342,23 @@
      */
     private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
 
+    final AppProfiler mAppProfiler;
+
+    private static final int INDEX_NATIVE_PSS = 0;
+    private static final int INDEX_NATIVE_SWAP_PSS = 1;
+    private static final int INDEX_NATIVE_RSS = 2;
+    private static final int INDEX_DALVIK_PSS = 3;
+    private static final int INDEX_DALVIK_SWAP_PSS = 4;
+    private static final int INDEX_DALVIK_RSS = 5;
+    private static final int INDEX_OTHER_PSS = 6;
+    private static final int INDEX_OTHER_SWAP_PSS = 7;
+    private static final int INDEX_OTHER_RSS = 8;
+    private static final int INDEX_TOTAL_PSS = 9;
+    private static final int INDEX_TOTAL_SWAP_PSS = 10;
+    private static final int INDEX_TOTAL_RSS = 11;
+    private static final int INDEX_TOTAL_NATIVE_PSS = 12;
+    private static final int INDEX_LAST = 13;
+
     final class UiHandler extends Handler {
         public UiHandler() {
             super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1754,63 +1550,10 @@
                 break;
             }
             case POST_DUMP_HEAP_NOTIFICATION_MSG: {
-                final String procName;
-                final int uid;
-                final long memLimit;
-                final String reportPackage;
-                final boolean isUserInitiated;
-                synchronized (ActivityManagerService.this) {
-                    uid = mMemWatchDumpUid;
-                    procName = mMemWatchDumpProcName;
-                    Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
-                    if (val == null) {
-                        val = mMemWatchProcesses.get(procName, 0);
-                    }
-                    if (val != null) {
-                        memLimit = val.first;
-                        reportPackage = val.second;
-                    } else {
-                        memLimit = 0;
-                        reportPackage = null;
-                    }
-                    isUserInitiated = mMemWatchIsUserInitiated;
-
-                    mMemWatchDumpUri = null;
-                    mMemWatchDumpProcName = null;
-                    mMemWatchDumpPid = -1;
-                    mMemWatchDumpUid = -1;
-                }
-                if (procName == null) {
-                    return;
-                }
-
-                if (DEBUG_PSS) Slog.d(TAG_PSS,
-                        "Showing dump heap notification from " + procName + "/" + uid);
-
-                Intent dumpFinishedIntent = new Intent(ACTION_HEAP_DUMP_FINISHED);
-                // Send this only to the Shell package.
-                dumpFinishedIntent.setPackage("com.android.shell");
-                dumpFinishedIntent.putExtra(Intent.EXTRA_UID, uid);
-                dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_IS_USER_INITIATED, isUserInitiated);
-                dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_SIZE_BYTES, memLimit);
-                dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_REPORT_PACKAGE, reportPackage);
-                dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_PROCESS_NAME, procName);
-
-                mContext.sendBroadcastAsUser(dumpFinishedIntent,
-                        UserHandle.getUserHandleForUid(uid));
+                mAppProfiler.handlePostDumpHeapNotification();
             } break;
             case ABORT_DUMPHEAP_MSG: {
-                String procName = (String) msg.obj;
-                if (procName != null) {
-                    synchronized (ActivityManagerService.this) {
-                        if (procName.equals(mMemWatchDumpProcName)) {
-                            mMemWatchDumpProcName = null;
-                            mMemWatchDumpUri = null;
-                            mMemWatchDumpPid = -1;
-                            mMemWatchDumpUid = -1;
-                        }
-                    }
-                }
+                mAppProfiler.handleAbortDumpHeap((String) msg.obj);
             } break;
             case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
                 IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
@@ -1843,147 +1586,6 @@
         }
     }
 
-    static final int COLLECT_PSS_BG_MSG = 1;
-    static final int DEFER_PSS_MSG = 2;
-    static final int STOP_DEFERRING_PSS_MSG = 3;
-
-    final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-            case COLLECT_PSS_BG_MSG: {
-                long start = SystemClock.uptimeMillis();
-                MemInfoReader memInfo = null;
-                synchronized (ActivityManagerService.this) {
-                    if (mFullPssPending) {
-                        mFullPssPending = false;
-                        memInfo = new MemInfoReader();
-                    }
-                }
-                if (memInfo != null) {
-                    updateCpuStatsNow();
-                    long nativeTotalPss = 0;
-                    final List<ProcessCpuTracker.Stats> stats;
-                    synchronized (mProcessCpuTracker) {
-                        stats = mProcessCpuTracker.getStats( (st)-> {
-                            return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
-                        });
-                    }
-                    final int N = stats.size();
-                    for (int j = 0; j < N; j++) {
-                        synchronized (mPidsSelfLocked) {
-                            if (mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
-                                // This is one of our own processes; skip it.
-                                continue;
-                            }
-                        }
-                        nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
-                    }
-                    memInfo.readMemInfo();
-                    synchronized (mProcessStats.mLock) {
-                        if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in "
-                                + (SystemClock.uptimeMillis()-start) + "ms");
-                        final long cachedKb = memInfo.getCachedSizeKb();
-                        final long freeKb = memInfo.getFreeSizeKb();
-                        final long zramKb = memInfo.getZramTotalSizeKb();
-                        final long kernelKb = memInfo.getKernelUsedSizeKb();
-                        EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
-                                kernelKb*1024, nativeTotalPss*1024);
-                        mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
-                                nativeTotalPss);
-                    }
-                }
-
-                int num = 0;
-                long[] tmp = new long[3];
-                do {
-                    ProcessRecord proc;
-                    int procState;
-                    int statType;
-                    int pid = -1;
-                    long lastPssTime;
-                    synchronized (ActivityManagerService.this) {
-                        if (mPendingPssProcesses.size() <= 0) {
-                            if (mTestPssMode || DEBUG_PSS) Slog.d(TAG_PSS,
-                                    "Collected pss of " + num + " processes in "
-                                    + (SystemClock.uptimeMillis() - start) + "ms");
-                            mPendingPssProcesses.clear();
-                            return;
-                        }
-                        proc = mPendingPssProcesses.remove(0);
-                        procState = proc.pssProcState;
-                        statType = proc.pssStatType;
-                        lastPssTime = proc.lastPssTime;
-                        long now = SystemClock.uptimeMillis();
-                        if (proc.thread != null && procState == proc.setProcState
-                                && (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
-                                        < now) {
-                            pid = proc.pid;
-                        } else {
-                            ProcessList.abortNextPssTime(proc.procStateMemTracker);
-                            if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
-                                    ": still need " +
-                                    (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE-now) +
-                                    "ms until safe");
-                            proc = null;
-                            pid = 0;
-                        }
-                    }
-                    if (proc != null) {
-                        long startTime = SystemClock.currentThreadTimeMillis();
-                        // skip background PSS calculation of apps that are capturing
-                        // camera imagery
-                        final boolean usingCamera = isCameraActiveForUid(proc.uid);
-                        long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
-                        long endTime = SystemClock.currentThreadTimeMillis();
-                        synchronized (ActivityManagerService.this) {
-                            if (pss != 0 && proc.thread != null && proc.setProcState == procState
-                                    && proc.pid == pid && proc.lastPssTime == lastPssTime) {
-                                num++;
-                                ProcessList.commitNextPssTime(proc.procStateMemTracker);
-                                recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
-                                        statType, endTime-startTime, SystemClock.uptimeMillis());
-                            } else {
-                                ProcessList.abortNextPssTime(proc.procStateMemTracker);
-                                if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
-                                        ": " + (proc.thread == null ? "NO_THREAD " : "") +
-                                        (usingCamera ? "CAMERA " : "") +
-                                        (proc.pid != pid ? "PID_CHANGED " : "") +
-                                        " initState=" + procState + " curState=" +
-                                        proc.setProcState + " " +
-                                        (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
-                            }
-                        }
-                    }
-                } while (true);
-            }
-
-            case DEFER_PSS_MSG: {
-                deferPssForActivityStart();
-            } break;
-
-            case STOP_DEFERRING_PSS_MSG: {
-                final int nesting = mActivityStartingNesting.decrementAndGet();
-                if (nesting <= 0) {
-                    if (DEBUG_PSS) {
-                        Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
-                                + nesting);
-                    }
-                    if (nesting < 0) {
-                        Slog.wtf(TAG, "Activity start nesting undercount!");
-                        mActivityStartingNesting.incrementAndGet();
-                    }
-                } else {
-                    if (DEBUG_PSS) {
-                        Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
-                    }
-                }
-            }
-            break;
-
-            }
-        }
-    };
 
     public void setSystemProcess() {
         try {
@@ -1994,10 +1596,7 @@
                     DUMP_FLAG_PRIORITY_HIGH);
             ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
             ServiceManager.addService("dbinfo", new DbBinder(this));
-            if (MONITOR_CPU_USAGE) {
-                ServiceManager.addService("cpuinfo", new CpuBinder(this),
-                        /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
-            }
+            mAppProfiler.setCpuInfoService();
             ServiceManager.addService("permission", new PermissionController(this));
             ServiceManager.addService("processinfo", new ProcessInfoService(this));
             ServiceManager.addService("cacheinfo", new CacheBinder(this));
@@ -2154,37 +1753,6 @@
         }
     }
 
-    static class CpuBinder extends Binder {
-        ActivityManagerService mActivityManagerService;
-        private final PriorityDump.PriorityDumper mPriorityDumper =
-                new PriorityDump.PriorityDumper() {
-            @Override
-            public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
-                    boolean asProto) {
-                if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
-                        "cpuinfo", pw)) return;
-                synchronized (mActivityManagerService.mProcessCpuTracker) {
-                    if (asProto) {
-                        mActivityManagerService.mProcessCpuTracker.dumpProto(fd);
-                        return;
-                    }
-                    pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
-                    pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
-                            SystemClock.uptimeMillis()));
-                }
-            }
-        };
-
-        CpuBinder(ActivityManagerService activityManagerService) {
-            mActivityManagerService = activityManagerService;
-        }
-
-        @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            PriorityDump.dump(mPriorityDumper, fd, pw, args);
-        }
-    }
-
     static class CacheBinder extends Binder {
         ActivityManagerService mActivityManagerService;
 
@@ -2427,13 +1995,12 @@
         mPlatformCompat = null;
         mProcessList = injector.getProcessList(this);
         mProcessList.init(this, activeUids, mPlatformCompat);
-        mLowMemDetector = null;
+        mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null);
         mOomAdjuster = hasHandlerThread
                 ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
 
         mIntentFirewall = hasHandlerThread
                 ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
-        mProcessCpuThread = null;
         mProcessStats = null;
         mCpHelper = new ContentProviderHelper(this, false);
         // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
@@ -2484,7 +2051,8 @@
                 Context.PLATFORM_COMPAT_SERVICE);
         mProcessList = mInjector.getProcessList(this);
         mProcessList.init(this, activeUids, mPlatformCompat);
-        mLowMemDetector = new LowMemDetector(this);
+        mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(),
+                new LowMemDetector(this));
         mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
 
         // Broadcast policy parameters
@@ -2556,40 +2124,6 @@
                 DisplayThread.get().getLooper());
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 
-        mProcessCpuThread = new Thread("CpuTracker") {
-            @Override
-            public void run() {
-                synchronized (mProcessCpuTracker) {
-                    mProcessCpuInitLatch.countDown();
-                    mProcessCpuTracker.init();
-                }
-                while (true) {
-                    try {
-                        try {
-                            synchronized(this) {
-                                final long now = SystemClock.uptimeMillis();
-                                long nextCpuDelay = (mLastCpuTime.get()+MONITOR_CPU_MAX_TIME)-now;
-                                long nextWriteDelay = (mLastWriteTime+BATTERY_STATS_TIME)-now;
-                                //Slog.i(TAG, "Cpu delay=" + nextCpuDelay
-                                //        + ", write delay=" + nextWriteDelay);
-                                if (nextWriteDelay < nextCpuDelay) {
-                                    nextCpuDelay = nextWriteDelay;
-                                }
-                                if (nextCpuDelay > 0) {
-                                    mProcessCpuMutexFree.set(true);
-                                    this.wait(nextCpuDelay);
-                                }
-                            }
-                        } catch (InterruptedException e) {
-                        }
-                        updateCpuStatsNow();
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Unexpected exception collecting process stats", e);
-                    }
-                }
-            }
-        };
-
         mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
 
         Watchdog.getInstance().addMonitor(this);
@@ -2623,7 +2157,6 @@
 
     private void start() {
         removeAllProcessGroups();
-        mProcessCpuThread.start();
 
         mBatteryStatsService.publish();
         mAppOpsService.publish();
@@ -2631,16 +2164,7 @@
         LocalServices.addService(ActivityManagerInternal.class, mInternal);
         mActivityTaskManager.onActivityManagerInternalAdded();
         mPendingIntentController.onActivityManagerInternalAdded();
-        // Wait for the synchronized block started in mProcessCpuThread,
-        // so that any other access to mProcessCpuTracker from main thread
-        // will be blocked during mProcessCpuTracker initialization.
-        try {
-            mProcessCpuInitLatch.await();
-        } catch (InterruptedException e) {
-            Slog.wtf(TAG, "Interrupted wait during start", e);
-            Thread.currentThread().interrupt();
-            throw new IllegalStateException("Interrupted wait during start");
-        }
+        mAppProfiler.onActivityManagerInternalAdded();
     }
 
     public void initPowerManagement() {
@@ -2783,111 +2307,12 @@
         }
     }
 
-    void updateCpuStats() {
-        final long now = SystemClock.uptimeMillis();
-        if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
-            return;
-        }
-        if (mProcessCpuMutexFree.compareAndSet(true, false)) {
-            synchronized (mProcessCpuThread) {
-                mProcessCpuThread.notify();
-            }
-        }
+    void updateCpuStatsLocked() {
+        mAppProfiler.updateCpuStatsLocked();
     }
 
     void updateCpuStatsNow() {
-        synchronized (mProcessCpuTracker) {
-            mProcessCpuMutexFree.set(false);
-            final long now = SystemClock.uptimeMillis();
-            boolean haveNewCpuStats = false;
-
-            if (MONITOR_CPU_USAGE &&
-                    mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) {
-                mLastCpuTime.set(now);
-                mProcessCpuTracker.update();
-                if (mProcessCpuTracker.hasGoodLastStats()) {
-                    haveNewCpuStats = true;
-                    //Slog.i(TAG, mProcessCpu.printCurrentState());
-                    //Slog.i(TAG, "Total CPU usage: "
-                    //        + mProcessCpu.getTotalCpuPercent() + "%");
-
-                    // Slog the cpu usage if the property is set.
-                    if ("true".equals(SystemProperties.get("events.cpu"))) {
-                        int user = mProcessCpuTracker.getLastUserTime();
-                        int system = mProcessCpuTracker.getLastSystemTime();
-                        int iowait = mProcessCpuTracker.getLastIoWaitTime();
-                        int irq = mProcessCpuTracker.getLastIrqTime();
-                        int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
-                        int idle = mProcessCpuTracker.getLastIdleTime();
-
-                        int total = user + system + iowait + irq + softIrq + idle;
-                        if (total == 0) total = 1;
-
-                        EventLogTags.writeCpu(
-                                ((user + system + iowait + irq + softIrq) * 100) / total,
-                                (user * 100) / total,
-                                (system * 100) / total,
-                                (iowait * 100) / total,
-                                (irq * 100) / total,
-                                (softIrq * 100) / total);
-                    }
-                }
-            }
-
-            final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
-            synchronized(bstats) {
-                synchronized(mPidsSelfLocked) {
-                    if (haveNewCpuStats) {
-                        if (bstats.startAddingCpuLocked()) {
-                            int totalUTime = 0;
-                            int totalSTime = 0;
-                            final int N = mProcessCpuTracker.countStats();
-                            for (int i=0; i<N; i++) {
-                                ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
-                                if (!st.working) {
-                                    continue;
-                                }
-                                ProcessRecord pr = mPidsSelfLocked.get(st.pid);
-                                totalUTime += st.rel_utime;
-                                totalSTime += st.rel_stime;
-                                if (pr != null) {
-                                    BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
-                                    if (ps == null || !ps.isActive()) {
-                                        pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
-                                                pr.info.uid, pr.processName);
-                                    }
-                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
-                                    pr.curCpuTime += st.rel_utime + st.rel_stime;
-                                    if (pr.lastCpuTime == 0) {
-                                        pr.lastCpuTime = pr.curCpuTime;
-                                    }
-                                } else {
-                                    BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
-                                    if (ps == null || !ps.isActive()) {
-                                        st.batteryStats = ps = bstats.getProcessStatsLocked(
-                                                bstats.mapUid(st.uid), st.name);
-                                    }
-                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
-                                }
-                            }
-                            final int userTime = mProcessCpuTracker.getLastUserTime();
-                            final int systemTime = mProcessCpuTracker.getLastSystemTime();
-                            final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime();
-                            final int irqTime = mProcessCpuTracker.getLastIrqTime();
-                            final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
-                            final int idleTime = mProcessCpuTracker.getLastIdleTime();
-                            bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
-                                    systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
-                        }
-                    }
-                }
-
-                if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
-                    mLastWriteTime = now;
-                    mBatteryStatsService.scheduleWriteToDisk();
-                }
-            }
-        }
+        mAppProfiler.updateCpuStatsNow();
     }
 
     @Override
@@ -3076,18 +2501,8 @@
             Slog.d(TAG_SWITCH,
                     "updateBatteryStats: comp=" + activity + "res=" + resumed);
         }
-        final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
-        FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
-                uid, activity.getPackageName(), activity.getShortClassName(),
-                resumed ? FrameworkStatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
-                        FrameworkStatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
-        synchronized (stats) {
-            if (resumed) {
-                stats.noteActivityResumedLocked(uid);
-            } else {
-                stats.noteActivityPausedLocked(uid);
-            }
-        }
+        mBatteryStatsService.updateBatteryStatsOnActivityUsage(activity.getPackageName(),
+                activity.getShortClassName(), uid, userId, resumed);
     }
 
     /**
@@ -3517,9 +2932,7 @@
             }
         }
 
-        if (mProfileData.getProfileProc() == app) {
-            clearProfilerLocked();
-        }
+        mAppProfiler.onAppDiedLocked(app);
 
         mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
             Slog.w(TAG, "Crash of app " + app.processName
@@ -3563,7 +2976,7 @@
         // and the app that died was not running instrumentation,
         // then tell everyone we are now low on memory.
         if (!mProcessList.haveBackgroundProcessLocked()) {
-            boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+            boolean doReport = Build.IS_DEBUGGABLE;
             if (doReport) {
                 long now = SystemClock.uptimeMillis();
                 if (now < (mLastMemUsageReportTime+5*60*1000)) {
@@ -3626,10 +3039,7 @@
             }
         }
 
-        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
-        synchronized (stats) {
-            stats.noteProcessDiedLocked(app.info.uid, pid);
-        }
+        mBatteryStatsService.noteProcessDied(app.info.uid, pid);
 
         if (!app.killed) {
             if (!fromBinderDied) {
@@ -3651,11 +3061,11 @@
                         "Process " + app.processName + " (pid " + pid + ") has died: "
                                 + ProcessList.makeOomAdjString(app.setAdj, true) + " "
                                 + ProcessList.makeProcStateString(app.setProcState), app.info.uid);
-                mAllowLowerMemLevel = true;
+                mAppProfiler.setAllowLowerMemLevelLocked(true);
             } else {
                 // Note that we always want to do oom adj to update our state with the
                 // new number of procs.
-                mAllowLowerMemLevel = false;
+                mAppProfiler.setAllowLowerMemLevelLocked(false);
                 doLowMem = false;
             }
             EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
@@ -4151,7 +3561,7 @@
             synchronized (this) {
                 // Allow memory level to go down (the flag needs to be set before updating oom adj)
                 // because this method is also used to simulate low memory.
-                mAllowLowerMemLevel = true;
+                mAppProfiler.setAllowLowerMemLevelLocked(true);
                 mProcessList.killPackageProcessesLocked(null /* packageName */, -1 /* appId */,
                         UserHandle.USER_ALL, ProcessList.CACHED_APP_MIN_ADJ,
                         ApplicationExitInfo.REASON_USER_REQUESTED,
@@ -4936,48 +4346,7 @@
             ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
             app.compat = compatibilityInfoForPackage(appInfo);
 
-            ProfilerInfo profilerInfo = null;
-            String preBindAgent = null;
-            if (mProfileData.getProfileApp() != null
-                    && mProfileData.getProfileApp().equals(processName)) {
-                mProfileData.setProfileProc(app);
-                if (mProfileData.getProfilerInfo() != null) {
-                    // Send a profiler info object to the app if either a file is given, or
-                    // an agent should be loaded at bind-time.
-                    boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
-                            || mProfileData.getProfilerInfo().attachAgentDuringBind;
-                    profilerInfo = needsInfo
-                            ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
-                    if (mProfileData.getProfilerInfo().agent != null) {
-                        preBindAgent = mProfileData.getProfilerInfo().agent;
-                    }
-                }
-            } else if (instr != null && instr.mProfileFile != null) {
-                profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
-                        null, false);
-            }
-            if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
-                // We need to do a debuggable check here. See setAgentApp for why the check is
-                // postponed to here.
-                if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                    String agent = mAppAgentMap.get(processName);
-                    // Do not overwrite already requested agent.
-                    if (profilerInfo == null) {
-                        profilerInfo = new ProfilerInfo(null, null, 0, false, false,
-                                mAppAgentMap.get(processName), true);
-                    } else if (profilerInfo.agent == null) {
-                        profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
-                    }
-                }
-            }
-
-            if (profilerInfo != null && profilerInfo.profileFd != null) {
-                profilerInfo.profileFd = profilerInfo.profileFd.dup();
-                if (TextUtils.equals(mProfileData.getProfileApp(), processName)
-                        && mProfileData.getProfilerInfo() != null) {
-                    clearProfilerLocked();
-                }
-            }
+            ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
 
             // We deprecated Build.SERIAL and it is not accessible to
             // Instant Apps and target APIs higher than O MR1. Since access to the serial
@@ -4986,44 +4355,6 @@
                     && appInfo.targetSdkVersion < Build.VERSION_CODES.P)
                             ? sTheRealBuildSerial : Build.UNKNOWN;
 
-            // Check if this is a secondary process that should be incorporated into some
-            // currently active instrumentation.  (Note we do this AFTER all of the profiling
-            // stuff above because profiling can currently happen only in the primary
-            // instrumentation process.)
-            if (mActiveInstrumentation.size() > 0 && instr == null) {
-                for (int i = mActiveInstrumentation.size() - 1;
-                        i >= 0 && app.getActiveInstrumentation() == null; i--) {
-                    ActiveInstrumentation aInstr = mActiveInstrumentation.get(i);
-                    if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
-                        if (aInstr.mTargetProcesses.length == 0) {
-                            // This is the wildcard mode, where every process brought up for
-                            // the target instrumentation should be included.
-                            if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
-                                app.setActiveInstrumentation(aInstr);
-                                aInstr.mRunningProcesses.add(app);
-                            }
-                        } else {
-                            for (String proc : aInstr.mTargetProcesses) {
-                                if (proc.equals(app.processName)) {
-                                    app.setActiveInstrumentation(aInstr);
-                                    aInstr.mRunningProcesses.add(app);
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            // If we were asked to attach an agent on startup, do so now, before we're binding
-            // application code.
-            if (preBindAgent != null) {
-                thread.attachAgent(preBindAgent);
-            }
-            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                thread.attachStartupAgents(app.info.dataDir);
-            }
-
             // Figure out whether the app needs to run in autofill compat mode.
             AutofillOptions autofillOptions = null;
             if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) {
@@ -5312,7 +4643,8 @@
                                 boolean sticky, int sendingUser) {
                             synchronized (ActivityManagerService.this) {
                                 mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
-                                requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+                                mAppProfiler.requestPssAllProcsLocked(
+                                        SystemClock.uptimeMillis(), true, false);
                             }
                         }
                     });
@@ -6418,8 +5750,8 @@
     }
 
     @Override
-    public List<StackInfo> getAllStackInfos() {
-        return mActivityTaskManager.getAllStackInfos();
+    public List<RootTaskInfo> getAllRootTaskInfos() {
+        return mActivityTaskManager.getAllRootTaskInfos();
     }
 
     @Override
@@ -6912,32 +6244,13 @@
                         "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
             }
 
-            if (agent == null) {
-                if (mAppAgentMap != null) {
-                    mAppAgentMap.remove(packageName);
-                    if (mAppAgentMap.isEmpty()) {
-                        mAppAgentMap = null;
-                    }
-                }
-            } else {
-                if (mAppAgentMap == null) {
-                    mAppAgentMap = new HashMap<>();
-                }
-                if (mAppAgentMap.size() >= 100) {
-                    // Limit the size of the map, to avoid OOMEs.
-                    Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
-                            + "/" + agent);
-                    return;
-                }
-                mAppAgentMap.put(packageName, agent);
-            }
+            mAppProfiler.setAgentAppLocked(packageName, agent);
         }
     }
 
     void setTrackAllocationApp(ApplicationInfo app, String processName) {
         synchronized (this) {
-            boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
-            if (!isDebuggable) {
+            if (!Build.IS_DEBUGGABLE) {
                 if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
                     throw new SecurityException("Process not debuggable: " + app.packageName);
                 }
@@ -6949,8 +6262,7 @@
 
     void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo) {
         synchronized (this) {
-            boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
-            if (!isDebuggable) {
+            if (!Build.IS_DEBUGGABLE) {
                 boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
                 boolean isAppProfileable = app.isProfileableByShell();
                 if (!isAppDebuggable && !isAppProfileable) {
@@ -6958,24 +6270,12 @@
                             + "and not profileable by shell: " + app.packageName);
                 }
             }
-            mProfileData.setProfileApp(processName);
-
-            if (mProfileData.getProfilerInfo() != null) {
-                if (mProfileData.getProfilerInfo().profileFd != null) {
-                    try {
-                        mProfileData.getProfilerInfo().profileFd.close();
-                    } catch (IOException e) {
-                    }
-                }
-            }
-            mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
-            mProfileType = 0;
+            mAppProfiler.setProfileAppLocked(processName, profilerInfo);
         }
     }
 
     void setNativeDebuggingAppLocked(ApplicationInfo app, String processName) {
-        boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
-        if (!isDebuggable) {
+        if (!Build.IS_DEBUGGABLE) {
             if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
                 throw new SecurityException("Process not debuggable: " + app.packageName);
             }
@@ -7057,7 +6357,7 @@
             return;
         }
         synchronized (this) {
-            startHeapDumpLocked(pr, true);
+            mAppProfiler.startHeapDumpLocked(pr, true);
         }
     }
 
@@ -7300,53 +6600,6 @@
         return uidRecord != null && !uidRecord.setIdle;
     }
 
-    /**
-     * Change the sched policy cgroup of a thread.
-     *
-     * <p>It is intended to be used in jni call of {@link Process#setThreadPriority(int, int)}.
-     * When a process is changing the priority of its threads, the sched policy of that
-     * thread may need to be changed accordingly - if the priority is changed to below
-     * or equal to {@link android.os.Process#THREAD_PRIORITY_BACKGROUND} or raising from it.
-     * However, because of the limitation of sepolicy, the thread priority change will
-     * fail for some processes. To solve it, we add this binder call in Activity Manager,
-     * so that the jni call of {@link Process#setThreadPriority(int, int)} could use it.
-     *
-     * @param tid tid of the thread to be changed.
-     * @param group The sched policy group to be changed to.
-     *
-     * @throws IllegalArgumentException if group is invalid.
-     * @throws SecurityException if no permission.
-     *
-     * @return Returns {@code true} if the sched policy is changed successfully;
-     *         {@code false} otherwise.
-     */
-    @Override
-    public boolean setSchedPolicyCgroup(final int tid, final int group) {
-        final int pid = Binder.getCallingPid();
-        final int tgid = Process.getThreadGroupLeader(tid);
-        final int pgroup = Process.getProcessGroup(pid);
-
-        // tid is not in the thread group of caller
-        if (pid != tgid) {
-            return false;
-        }
-
-        // sched group is higher than its main process
-        if (group > pgroup) {
-            return false;
-        }
-
-        try {
-            Process.setThreadGroup(tid, group);
-            return true;
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Failed to set thread group, argument invalid:\n" + e);
-        } catch (SecurityException e) {
-            Slog.w(TAG, "Failed to set thread group, no permission:\n" + e);
-        }
-        return false;
-    }
-
     @Override
     public void setPersistentVrThread(int tid) {
         mActivityTaskManager.setPersistentVrThread(tid);
@@ -7880,10 +7133,6 @@
         br.onReceive(mContext, intent);
     }
 
-    private long getLowRamTimeSinceIdle(long now) {
-        return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now-mLowRamStartTime) : 0);
-    }
-
     @Override
     public void performIdleMaintenance() {
         if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
@@ -7899,12 +7148,9 @@
             // Compact all non-zygote processes to freshen up the page cache.
             mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
 
-            final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now);
+            final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLocked(now);
             mLastIdleTime = now;
-            mLowRamTimeSinceLastIdle = 0;
-            if (mLowRamStartTime != 0) {
-                mLowRamStartTime = now;
-            }
+            mAppProfiler.updateLowRamTimestampLocked(now);
 
             StringBuilder sb = new StringBuilder(128);
             sb.append("Idle maintenance over ");
@@ -7955,8 +7201,7 @@
                         && proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
                     proc.notCachedSinceIdle = true;
                     proc.initialIdlePss = 0;
-                    proc.nextPssTime = ProcessList.computeNextPssTime(proc.setProcState, null,
-                            mTestPssMode, mAtmInternal.isSleeping(), now);
+                    mAppProfiler.updateNextPssTimeLocked(proc.setProcState, proc, now, true);
                 }
             }
         }
@@ -7994,11 +7239,7 @@
                 NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
         mHiddenApiBlacklist.registerObserver();
 
-        final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                ActivityThread.currentApplication().getMainExecutor(),
-                mPssDelayConfigListener);
+        mAppProfiler.retrieveSettings();
 
         synchronized (this) {
             mDebugApp = mOrigDebugApp = debugApp;
@@ -8017,7 +7258,6 @@
             mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers,
                     delayUserDataLocking);
             mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
-            mPssDeferralTime = pssDeferralMs;
         }
     }
 
@@ -9007,7 +8247,7 @@
     public int getMemoryTrimLevel() {
         enforceNotIsolatedCaller("getMyMemoryState");
         synchronized (this) {
-            return mLastMemoryLevel;
+            return mAppProfiler.getLastMemoryLevelLocked();
         }
     }
 
@@ -10018,7 +9258,7 @@
         needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
 
         needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
-                mTestPssMode, mWakefulness);
+                mAppProfiler.getTestPssModeLocked(), mWakefulness);
 
         if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
             if (needSep) pw.println();
@@ -10062,35 +9302,7 @@
                         + " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
             }
         }
-        if (mMemWatchProcesses.getMap().size() > 0) {
-            pw.println("  Mem watch processes:");
-            final ArrayMap<String, SparseArray<Pair<Long, String>>> procs
-                    = mMemWatchProcesses.getMap();
-            for (int i=0; i<procs.size(); i++) {
-                final String proc = procs.keyAt(i);
-                final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
-                for (int j=0; j<uids.size(); j++) {
-                    if (needSep) {
-                        pw.println();
-                        needSep = false;
-                    }
-                    StringBuilder sb = new StringBuilder();
-                    sb.append("    ").append(proc).append('/');
-                    UserHandle.formatUid(sb, uids.keyAt(j));
-                    Pair<Long, String> val = uids.valueAt(j);
-                    sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
-                    if (val.second != null) {
-                        sb.append(", report to ").append(val.second);
-                    }
-                    pw.println(sb.toString());
-                }
-            }
-            pw.print("  mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
-            pw.print("  mMemWatchDumpUri="); pw.println(mMemWatchDumpUri);
-            pw.print("  mMemWatchDumpPid="); pw.println(mMemWatchDumpPid);
-            pw.print("  mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
-            pw.print("  mMemWatchIsUserInitiated="); pw.println(mMemWatchIsUserInitiated);
-        }
+        needSep = mAppProfiler.dumpMemWatchProcessesLocked(pw, needSep);
         if (mTrackAllocationApp != null) {
             if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
                 if (needSep) {
@@ -10100,29 +9312,7 @@
                 pw.println("  mTrackAllocationApp=" + mTrackAllocationApp);
             }
         }
-        if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
-                || (mProfileData.getProfilerInfo() != null &&
-                (mProfileData.getProfilerInfo().profileFile != null
-                        || mProfileData.getProfilerInfo().profileFd != null))) {
-            if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
-                if (needSep) {
-                    pw.println();
-                    needSep = false;
-                }
-                pw.println("  mProfileApp=" + mProfileData.getProfileApp()
-                        + " mProfileProc=" + mProfileData.getProfileProc());
-                if (mProfileData.getProfilerInfo() != null) {
-                    pw.println("  mProfileFile=" + mProfileData.getProfilerInfo().profileFile
-                            + " mProfileFd=" + mProfileData.getProfilerInfo().profileFd);
-                    pw.println("  mSamplingInterval="
-                            + mProfileData.getProfilerInfo().samplingInterval +
-                            " mAutoStopProfiler="
-                            + mProfileData.getProfilerInfo().autoStopProfiler +
-                            " mStreamingOutput=" + mProfileData.getProfilerInfo().streamingOutput);
-                    pw.println("  mProfileType=" + mProfileType);
-                }
-            }
-        }
+        needSep = mAppProfiler.dumpProfileDataLocked(pw, dumpPackage, needSep);
         if (mNativeDebuggingApp != null) {
             if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
                 if (needSep) {
@@ -10150,14 +9340,13 @@
                         pw.println("");
                 mOomAdjuster.dumpSequenceNumbersLocked(pw);
                 mOomAdjuster.dumpProcCountsLocked(pw);
-                pw.println("  mAllowLowerMemLevel=" + mAllowLowerMemLevel
-                        + " mLastMemoryLevel=" + mLastMemoryLevel
-                        + " mLastNumProcesses=" + mLastNumProcesses);
+                mAppProfiler.dumpMemoryLevelsLocked(pw);
                 long now = SystemClock.uptimeMillis();
                 pw.print("  mLastIdleTime=");
                         TimeUtils.formatDuration(now, mLastIdleTime, pw);
                         pw.print(" mLowRamSinceLastIdle=");
-                        TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw);
+                        TimeUtils.formatDuration(
+                                mAppProfiler.getLowRamTimeSinceIdleLocked(now), pw);
                         pw.println();
 
                 pw.println();
@@ -10305,7 +9494,8 @@
                 dumpPackage);
         mAppErrors.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
                 dumpPackage);
-        mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, mTestPssMode);
+        mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness,
+                mAppProfiler.getTestPssModeLocked());
 
         if (dumpPackage == null) {
             mUserController.dumpDebug(proto,
@@ -10342,42 +9532,7 @@
             }
         }
 
-        if (mMemWatchProcesses.getMap().size() > 0) {
-            final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
-            ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
-            for (int i=0; i<procs.size(); i++) {
-                final String proc = procs.keyAt(i);
-                final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
-                final long ptoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
-                proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME, proc);
-                for (int j=0; j<uids.size(); j++) {
-                    final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MEM_STATS);
-                    Pair<Long, String> val = uids.valueAt(j);
-                    proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
-                    proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
-                            DebugUtils.sizeValueToString(val.first, new StringBuilder()));
-                    proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
-                    proto.end(utoken);
-                }
-                proto.end(ptoken);
-            }
-
-            final long dtoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
-            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME,
-                    mMemWatchDumpProcName);
-            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.URI,
-                    mMemWatchDumpUri.toString());
-            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID,
-                    mMemWatchDumpPid);
-            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID,
-                    mMemWatchDumpUid);
-            proto.write(
-                    ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.IS_USER_INITIATED,
-                    mMemWatchIsUserInitiated);
-            proto.end(dtoken);
-
-            proto.end(token);
-        }
+        mAppProfiler.writeMemWatchProcessToProtoLocked(proto);
 
         if (mTrackAllocationApp != null) {
             if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
@@ -10386,25 +9541,7 @@
             }
         }
 
-        if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
-                || (mProfileData.getProfilerInfo() != null &&
-                (mProfileData.getProfilerInfo().profileFile != null
-                        || mProfileData.getProfilerInfo().profileFd != null))) {
-            if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
-                final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
-                proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME,
-                        mProfileData.getProfileApp());
-                mProfileData.getProfileProc().dumpDebug(proto,
-                        ActivityManagerServiceDumpProcessesProto.Profile.PROC);
-                if (mProfileData.getProfilerInfo() != null) {
-                    mProfileData.getProfilerInfo().dumpDebug(proto,
-                            ActivityManagerServiceDumpProcessesProto.Profile.INFO);
-                    proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE,
-                            mProfileType);
-                }
-                proto.end(token);
-            }
-        }
+        mAppProfiler.writeProfileDataToProtoLocked(proto, dumpPackage);
 
         if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
             proto.write(ActivityManagerServiceDumpProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
@@ -10422,12 +9559,11 @@
             proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
             mOomAdjuster.dumpProcessListVariablesLocked(proto);
-            proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+            mAppProfiler.writeMemoryLevelsToProtoLocked(proto);
             long now = SystemClock.uptimeMillis();
             ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
+            proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS,
+                    mAppProfiler.getLowRamTimeSinceIdleLocked(now));
         }
     }
 
@@ -11610,7 +10746,7 @@
     }
 
     private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
-            MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
+            MemoryUsageDumpOptions opts, final String[] innerArgs, boolean brief,
             ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
@@ -11630,16 +10766,13 @@
                         findPid = Integer.parseInt(innerArgs[0]);
                     } catch (NumberFormatException e) {
                     }
-                    synchronized (mProcessCpuTracker) {
-                        final int N = mProcessCpuTracker.countStats();
-                        for (int i=0; i<N; i++) {
-                            ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
-                            if (st.pid == findPid || (st.baseName != null
-                                    && st.baseName.equals(innerArgs[0]))) {
-                                nativeProcs.add(st);
-                            }
+                    final int fFindPid = findPid;
+                    mAppProfiler.forAllCpuStats((st) -> {
+                        if (st.pid == fFindPid || (st.baseName != null
+                                && st.baseName.equals(innerArgs[0]))) {
+                            nativeProcs.add(st);
                         }
-                    }
+                    });
                     if (nativeProcs.size() > 0) {
                         dumpApplicationMemoryUsageHeader(pw, uptime, realtime,
                                 opts.isCheckinRequest, opts.isCompact);
@@ -11697,21 +10830,13 @@
 
         ArrayList<MemItem> procMems = new ArrayList<MemItem>();
         final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
-        long nativePss = 0;
-        long nativeSwapPss = 0;
-        long nativeRss = 0;
-        long dalvikPss = 0;
-        long dalvikSwapPss = 0;
-        long dalvikRss = 0;
+        final long[] ss = new long[INDEX_LAST];
         long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
         long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
         long[] dalvikSubitemRss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
-        long otherPss = 0;
-        long otherSwapPss = 0;
-        long otherRss = 0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
         long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
         long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
@@ -11722,7 +10847,6 @@
         ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
                 new ArrayList[DUMP_MEM_OOM_LABEL.length];
 
-        long totalPss = 0;
         long totalSwapPss = 0;
         long totalRss = 0;
         long cachedPss = 0;
@@ -11831,40 +10955,40 @@
                 }
 
                 if (!opts.isCheckinRequest && mi != null) {
-                    totalPss += myTotalPss;
-                    totalSwapPss += myTotalSwapPss;
-                    totalRss += myTotalRss;
+                    ss[INDEX_TOTAL_PSS] += myTotalPss;
+                    ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+                    ss[INDEX_TOTAL_RSS] += myTotalRss;
                     MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
                             (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
                             myTotalSwapPss, myTotalRss, pid, hasActivities);
                     procMems.add(pssItem);
                     procMemsMap.put(pid, pssItem);
 
-                    nativePss += mi.nativePss;
-                    nativeSwapPss += mi.nativeSwappedOutPss;
-                    nativeRss += mi.nativeRss;
-                    dalvikPss += mi.dalvikPss;
-                    dalvikSwapPss += mi.dalvikSwappedOutPss;
-                    dalvikRss += mi.dalvikRss;
+                    ss[INDEX_NATIVE_PSS] += mi.nativePss;
+                    ss[INDEX_NATIVE_SWAP_PSS] += mi.nativeSwappedOutPss;
+                    ss[INDEX_NATIVE_RSS] += mi.nativeRss;
+                    ss[INDEX_DALVIK_PSS] += mi.dalvikPss;
+                    ss[INDEX_DALVIK_SWAP_PSS] += mi.dalvikSwappedOutPss;
+                    ss[INDEX_DALVIK_RSS] += mi.dalvikRss;
                     for (int j=0; j<dalvikSubitemPss.length; j++) {
                         dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                         dalvikSubitemSwapPss[j] +=
                                 mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                         dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                     }
-                    otherPss += mi.otherPss;
-                    otherRss += mi.otherRss;
-                    otherSwapPss += mi.otherSwappedOutPss;
+                    ss[INDEX_OTHER_PSS] += mi.otherPss;
+                    ss[INDEX_OTHER_RSS] += mi.otherRss;
+                    ss[INDEX_OTHER_SWAP_PSS] += mi.otherSwappedOutPss;
                     for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                         long mem = mi.getOtherPss(j);
                         miscPss[j] += mem;
-                        otherPss -= mem;
+                        ss[INDEX_OTHER_PSS] -= mem;
                         mem = mi.getOtherSwappedOutPss(j);
                         miscSwapPss[j] += mem;
-                        otherSwapPss -= mem;
+                        ss[INDEX_OTHER_SWAP_PSS] -= mem;
                         mem = mi.getOtherRss(j);
                         miscRss[j] += mem;
-                        otherRss -= mem;
+                        ss[INDEX_OTHER_RSS] -= mem;
                     }
 
                     if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
@@ -11894,86 +11018,87 @@
 
         if (collectNative) {
             mi = null;
-            synchronized (mProcessCpuTracker) {
-                final int N = mProcessCpuTracker.countStats();
-                for (int i=0; i<N; i++) {
-                    ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
-                    if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
-                        if (mi == null) {
-                            mi = new Debug.MemoryInfo();
-                        }
-                        if (!brief && !opts.oomOnly) {
-                            if (!Debug.getMemoryInfo(st.pid, mi)) {
-                                continue;
-                            }
-                        } else {
-                            long pss = Debug.getPss(st.pid, tmpLong, null);
-                            if (pss == 0) {
-                                continue;
-                            }
-                            mi.nativePss = (int) pss;
-                            mi.nativePrivateDirty = (int) tmpLong[0];
-                            mi.nativeRss = (int) tmpLong[2];
-                        }
-
-                        final long myTotalPss = mi.getTotalPss();
-                        final long myTotalSwapPss = mi.getTotalSwappedOutPss();
-                        final long myTotalRss = mi.getTotalRss();
-                        totalPss += myTotalPss;
-                        totalSwapPss += myTotalSwapPss;
-                        totalRss += myTotalRss;
-                        nativeProcTotalPss += myTotalPss;
-
-                        MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
-                                st.name, myTotalPss, mi.getSummaryTotalSwapPss(), myTotalRss,
-                                st.pid, false);
-                        procMems.add(pssItem);
-
-                        nativePss += mi.nativePss;
-                        nativeSwapPss += mi.nativeSwappedOutPss;
-                        nativeRss += mi.nativeRss;
-                        dalvikPss += mi.dalvikPss;
-                        dalvikSwapPss += mi.dalvikSwappedOutPss;
-                        dalvikRss += mi.dalvikRss;
-                        for (int j=0; j<dalvikSubitemPss.length; j++) {
-                            dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                            dalvikSubitemSwapPss[j] +=
-                                    mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                            dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
-                                    + j);
-                        }
-                        otherPss += mi.otherPss;
-                        otherSwapPss += mi.otherSwappedOutPss;
-                        otherRss += mi.otherRss;
-                        for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
-                            long mem = mi.getOtherPss(j);
-                            miscPss[j] += mem;
-                            otherPss -= mem;
-                            mem = mi.getOtherSwappedOutPss(j);
-                            miscSwapPss[j] += mem;
-                            otherSwapPss -= mem;
-                            mem = mi.getOtherRss(j);
-                            miscRss[j] += mem;
-                            otherRss -= mem;
-                        }
-                        oomPss[0] += myTotalPss;
-                        oomSwapPss[0] += myTotalSwapPss;
-                        if (oomProcs[0] == null) {
-                            oomProcs[0] = new ArrayList<MemItem>();
-                        }
-                        oomProcs[0].add(pssItem);
-                        oomRss[0] += myTotalRss;
+            final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
+            mAppProfiler.forAllCpuStats((st) -> {
+                if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+                    if (memInfos[0] == null) {
+                        memInfos[0] = new Debug.MemoryInfo();
                     }
+                    final Debug.MemoryInfo info = memInfos[0];
+                    if (!brief && !opts.oomOnly) {
+                        if (!Debug.getMemoryInfo(st.pid, info)) {
+                            return;
+                        }
+                    } else {
+                        long pss = Debug.getPss(st.pid, tmpLong, null);
+                        if (pss == 0) {
+                            return;
+                        }
+                        info.nativePss = (int) pss;
+                        info.nativePrivateDirty = (int) tmpLong[0];
+                        info.nativeRss = (int) tmpLong[2];
+                    }
+
+                    final long myTotalPss = info.getTotalPss();
+                    final long myTotalSwapPss = info.getTotalSwappedOutPss();
+                    final long myTotalRss = info.getTotalRss();
+                    ss[INDEX_TOTAL_PSS] += myTotalPss;
+                    ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+                    ss[INDEX_TOTAL_RSS] += myTotalRss;
+                    ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
+
+                    MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
+                            st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
+                            st.pid, false);
+                    procMems.add(pssItem);
+
+                    ss[INDEX_NATIVE_PSS] += info.nativePss;
+                    ss[INDEX_NATIVE_SWAP_PSS] += info.nativeSwappedOutPss;
+                    ss[INDEX_NATIVE_RSS] += info.nativeRss;
+                    ss[INDEX_DALVIK_PSS] += info.dalvikPss;
+                    ss[INDEX_DALVIK_SWAP_PSS] += info.dalvikSwappedOutPss;
+                    ss[INDEX_DALVIK_RSS] += info.dalvikRss;
+                    for (int j = 0; j < dalvikSubitemPss.length; j++) {
+                        dalvikSubitemPss[j] += info.getOtherPss(
+                                Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        dalvikSubitemSwapPss[j] +=
+                                info.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        dalvikSubitemRss[j] += info.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
+                                + j);
+                    }
+                    ss[INDEX_OTHER_PSS] += info.otherPss;
+                    ss[INDEX_OTHER_SWAP_PSS] += info.otherSwappedOutPss;
+                    ss[INDEX_OTHER_RSS] += info.otherRss;
+                    for (int j = 0; j < Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+                        long mem = info.getOtherPss(j);
+                        miscPss[j] += mem;
+                        ss[INDEX_OTHER_PSS] -= mem;
+                        mem = info.getOtherSwappedOutPss(j);
+                        miscSwapPss[j] += mem;
+                        ss[INDEX_OTHER_SWAP_PSS] -= mem;
+                        mem = info.getOtherRss(j);
+                        miscRss[j] += mem;
+                        ss[INDEX_OTHER_RSS] -= mem;
+                    }
+                    oomPss[0] += myTotalPss;
+                    oomSwapPss[0] += myTotalSwapPss;
+                    if (oomProcs[0] == null) {
+                        oomProcs[0] = new ArrayList<MemItem>();
+                    }
+                    oomProcs[0].add(pssItem);
+                    oomRss[0] += myTotalRss;
                 }
-            }
+            });
 
             ArrayList<MemItem> catMems = new ArrayList<MemItem>();
 
-            catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, nativeRss, -1));
+            catMems.add(new MemItem("Native", "Native",
+                    ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
             final int dalvikId = -2;
-            catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikRss,
-                    dalvikId));
-            catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, otherRss, -3));
+            catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS],
+                    ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId));
+            catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS],
+                    ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3));
             for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                 String label = Debug.MemoryInfo.getOtherLabel(j);
                 catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j],  j));
@@ -12039,7 +11164,7 @@
                 }
                 dumpMemItems(out, "  ", "cat", catMems, true, opts.isCompact, false, false);
             }
-            opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
+            opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && ss[INDEX_TOTAL_SWAP_PSS] != 0;
             if (!brief && !opts.oomOnly && !opts.isCompact) {
                 pw.println();
                 pw.println("Total PSS by process:");
@@ -12065,40 +11190,23 @@
             }
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
-            if (nativeProcTotalPss > 0) {
+            if (ss[INDEX_TOTAL_NATIVE_PSS] > 0) {
                 synchronized (mProcessStats.mLock) {
                     final long cachedKb = memInfo.getCachedSizeKb();
                     final long freeKb = memInfo.getFreeSizeKb();
                     final long zramKb = memInfo.getZramTotalSizeKb();
                     final long kernelKb = memInfo.getKernelUsedSizeKb();
-                    EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
-                            kernelKb*1024, nativeProcTotalPss*1024);
+                    EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+                            kernelKb * 1024, ss[INDEX_TOTAL_NATIVE_PSS] * 1024);
                     mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
-                            nativeProcTotalPss);
+                            ss[INDEX_TOTAL_NATIVE_PSS]);
                 }
             }
             if (!brief) {
                 if (!opts.isCompact) {
                     pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
                     pw.print(" (status ");
-                    switch (mLastMemoryLevel) {
-                        case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
-                            pw.println("normal)");
-                            break;
-                        case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
-                            pw.println("moderate)");
-                            break;
-                        case ProcessStats.ADJ_MEM_FACTOR_LOW:
-                            pw.println("low)");
-                            break;
-                        case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
-                            pw.println("critical)");
-                            break;
-                        default:
-                            pw.print(mLastMemoryLevel);
-                            pw.println(")");
-                            break;
-                    }
+                    mAppProfiler.dumpLastMemoryLevelLocked(pw);
                     pw.print(" Free RAM: ");
                     pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
                             + memInfo.getFreeSizeKb()));
@@ -12113,7 +11221,7 @@
                     pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(",");
                     pw.print(cachedPss + memInfo.getCachedSizeKb()
                             + memInfo.getFreeSizeKb()); pw.print(",");
-                    pw.println(totalPss - cachedPss);
+                    pw.println(ss[INDEX_TOTAL_PSS] - cachedPss);
                 }
             }
             long kernelUsed = memInfo.getKernelUsedSizeKb();
@@ -12135,13 +11243,14 @@
                 // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
                 kernelUsed += ionHeap;
             }
-            final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+            final long lostRAM = memInfo.getTotalSizeKb()
+                    - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
                     - kernelUsed - memInfo.getZramTotalSizeKb();
             if (!opts.isCompact) {
-                pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
+                pw.print(" Used RAM: "); pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss
                         + kernelUsed)); pw.print(" (");
-                pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
+                pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss)); pw.print(" used pss + ");
                 pw.print(stringifyKBSize(kernelUsed)); pw.print(" kernel)\n");
                 pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
             } else {
@@ -12215,7 +11324,7 @@
     }
 
     private final void dumpApplicationMemoryUsage(FileDescriptor fd,
-            MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
+            MemoryUsageDumpOptions opts, final String[] innerArgs, boolean brief,
             ArrayList<ProcessRecord> procs) {
         final long uptimeMs = SystemClock.uptimeMillis();
         final long realtimeMs = SystemClock.elapsedRealtime();
@@ -12227,7 +11336,7 @@
             if (innerArgs.length > 0) {
                 proc = innerArgs[0];
                 if (proc.charAt(0) != '-') {
-                    ArrayList<ProcessCpuTracker.Stats> nativeProcs
+                    final ArrayList<ProcessCpuTracker.Stats> nativeProcs
                             = new ArrayList<ProcessCpuTracker.Stats>();
                     updateCpuStatsNow();
                     int findPid = -1;
@@ -12235,16 +11344,13 @@
                         findPid = Integer.parseInt(innerArgs[0]);
                     } catch (NumberFormatException e) {
                     }
-                    synchronized (mProcessCpuTracker) {
-                        final int N = mProcessCpuTracker.countStats();
-                        for (int i=0; i<N; i++) {
-                            ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
-                            if (st.pid == findPid || (st.baseName != null
-                                    && st.baseName.equals(innerArgs[0]))) {
-                                nativeProcs.add(st);
-                            }
+                    final int fFindPid = findPid;
+                    mAppProfiler.forAllCpuStats((st) -> {
+                        if (st.pid == fFindPid || (st.baseName != null
+                                && st.baseName.equals(innerArgs[0]))) {
+                            nativeProcs.add(st);
                         }
-                    }
+                    });
                     if (nativeProcs.size() > 0) {
                         ProtoOutputStream proto = new ProtoOutputStream(fd);
 
@@ -12308,36 +11414,25 @@
         proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs);
         proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs);
 
-        ArrayList<MemItem> procMems = new ArrayList<MemItem>();
+        final ArrayList<MemItem> procMems = new ArrayList<MemItem>();
         final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
-        long nativePss = 0;
-        long nativeSwapPss = 0;
-        long nativeRss = 0;
-        long dalvikPss = 0;
-        long dalvikSwapPss = 0;
-        long dalvikRss = 0;
+        final long[] ss = new long[INDEX_LAST];
         long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
         long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
         long[] dalvikSubitemRss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
-        long otherPss = 0;
-        long otherSwapPss = 0;
-        long otherRss = 0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
         long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
         long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
 
-        long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
-        long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
-        long[] oomRss = new long[DUMP_MEM_OOM_LABEL.length];
-        ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
+        final long[] oomPss = new long[DUMP_MEM_OOM_LABEL.length];
+        final long[] oomSwapPss = new long[DUMP_MEM_OOM_LABEL.length];
+        final long[] oomRss = new long[DUMP_MEM_OOM_LABEL.length];
+        final ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
                 new ArrayList[DUMP_MEM_OOM_LABEL.length];
 
-        long totalPss = 0;
-        long totalSwapPss = 0;
-        long totalRss = 0;
         long cachedPss = 0;
         long cachedSwapPss = 0;
         boolean hasSwapPss = false;
@@ -12438,40 +11533,40 @@
             }
 
             if (!opts.isCheckinRequest && mi != null) {
-                totalPss += myTotalPss;
-                totalSwapPss += myTotalSwapPss;
-                totalRss += myTotalRss;
+                ss[INDEX_TOTAL_PSS] += myTotalPss;
+                ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+                ss[INDEX_TOTAL_RSS] += myTotalRss;
                 MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
                         (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
                         myTotalSwapPss, myTotalRss, pid, hasActivities);
                 procMems.add(pssItem);
                 procMemsMap.put(pid, pssItem);
 
-                nativePss += mi.nativePss;
-                nativeSwapPss += mi.nativeSwappedOutPss;
-                nativeRss += mi.nativeRss;
-                dalvikPss += mi.dalvikPss;
-                dalvikSwapPss += mi.dalvikSwappedOutPss;
-                dalvikRss += mi.dalvikRss;
+                ss[INDEX_NATIVE_PSS] += mi.nativePss;
+                ss[INDEX_NATIVE_SWAP_PSS] += mi.nativeSwappedOutPss;
+                ss[INDEX_NATIVE_RSS] += mi.nativeRss;
+                ss[INDEX_DALVIK_PSS] += mi.dalvikPss;
+                ss[INDEX_DALVIK_SWAP_PSS] += mi.dalvikSwappedOutPss;
+                ss[INDEX_DALVIK_RSS] += mi.dalvikRss;
                 for (int j=0; j<dalvikSubitemPss.length; j++) {
                     dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                     dalvikSubitemSwapPss[j] +=
                             mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                     dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                 }
-                otherPss += mi.otherPss;
-                otherRss += mi.otherRss;
-                otherSwapPss += mi.otherSwappedOutPss;
+                ss[INDEX_OTHER_PSS] += mi.otherPss;
+                ss[INDEX_OTHER_RSS] += mi.otherRss;
+                ss[INDEX_OTHER_SWAP_PSS] += mi.otherSwappedOutPss;
                 for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                     long mem = mi.getOtherPss(j);
                     miscPss[j] += mem;
-                    otherPss -= mem;
+                    ss[INDEX_OTHER_PSS] -= mem;
                     mem = mi.getOtherSwappedOutPss(j);
                     miscSwapPss[j] += mem;
-                    otherSwapPss -= mem;
+                    ss[INDEX_OTHER_SWAP_PSS] -= mem;
                     mem = mi.getOtherRss(j);
                     miscRss[j] += mem;
-                    otherRss -= mem;
+                    ss[INDEX_OTHER_RSS] -= mem;
                 }
 
                 if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
@@ -12500,86 +11595,87 @@
 
         if (collectNative) {
             mi = null;
-            synchronized (mProcessCpuTracker) {
-                final int N = mProcessCpuTracker.countStats();
-                for (int i=0; i<N; i++) {
-                    ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
-                    if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
-                        if (mi == null) {
-                            mi = new Debug.MemoryInfo();
-                        }
-                        if (!brief && !opts.oomOnly) {
-                            if (!Debug.getMemoryInfo(st.pid, mi)) {
-                                continue;
-                            }
-                        } else {
-                            long pss = Debug.getPss(st.pid, tmpLong, null);
-                            if (pss == 0) {
-                                continue;
-                            }
-                            mi.nativePss = (int) pss;
-                            mi.nativePrivateDirty = (int) tmpLong[0];
-                            mi.nativeRss = (int) tmpLong[2];
-                        }
-
-                        final long myTotalPss = mi.getTotalPss();
-                        final long myTotalSwapPss = mi.getTotalSwappedOutPss();
-                        final long myTotalRss = mi.getTotalRss();
-                        totalPss += myTotalPss;
-                        totalSwapPss += myTotalSwapPss;
-                        totalRss += myTotalRss;
-                        nativeProcTotalPss += myTotalPss;
-
-                        MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
-                                st.name, myTotalPss, mi.getSummaryTotalSwapPss(), myTotalRss,
-                                st.pid, false);
-                        procMems.add(pssItem);
-
-                        nativePss += mi.nativePss;
-                        nativeSwapPss += mi.nativeSwappedOutPss;
-                        nativeRss += mi.nativeRss;
-                        dalvikPss += mi.dalvikPss;
-                        dalvikSwapPss += mi.dalvikSwappedOutPss;
-                        dalvikRss += mi.dalvikRss;
-                        for (int j=0; j<dalvikSubitemPss.length; j++) {
-                            dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                            dalvikSubitemSwapPss[j] +=
-                                    mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                            dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
-                                    + j);
-                        }
-                        otherPss += mi.otherPss;
-                        otherSwapPss += mi.otherSwappedOutPss;
-                        otherRss += mi.otherRss;
-                        for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
-                            long mem = mi.getOtherPss(j);
-                            miscPss[j] += mem;
-                            otherPss -= mem;
-                            mem = mi.getOtherSwappedOutPss(j);
-                            miscSwapPss[j] += mem;
-                            otherSwapPss -= mem;
-                            mem = mi.getOtherRss(j);
-                            miscRss[j] += mem;
-                            otherRss -= mem;
-                        }
-                        oomPss[0] += myTotalPss;
-                        oomSwapPss[0] += myTotalSwapPss;
-                        if (oomProcs[0] == null) {
-                            oomProcs[0] = new ArrayList<MemItem>();
-                        }
-                        oomProcs[0].add(pssItem);
-                        oomRss[0] += myTotalRss;
+            final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
+            mAppProfiler.forAllCpuStats((st) -> {
+                if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+                    if (memInfos[0] == null) {
+                        memInfos[0] = new Debug.MemoryInfo();
                     }
+                    final Debug.MemoryInfo info = memInfos[0];
+                    if (!brief && !opts.oomOnly) {
+                        if (!Debug.getMemoryInfo(st.pid, info)) {
+                            return;
+                        }
+                    } else {
+                        long pss = Debug.getPss(st.pid, tmpLong, null);
+                        if (pss == 0) {
+                            return;
+                        }
+                        info.nativePss = (int) pss;
+                        info.nativePrivateDirty = (int) tmpLong[0];
+                        info.nativeRss = (int) tmpLong[2];
+                    }
+
+                    final long myTotalPss = info.getTotalPss();
+                    final long myTotalSwapPss = info.getTotalSwappedOutPss();
+                    final long myTotalRss = info.getTotalRss();
+                    ss[INDEX_TOTAL_PSS] += myTotalPss;
+                    ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
+                    ss[INDEX_TOTAL_RSS] += myTotalRss;
+                    ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
+
+                    MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
+                            st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
+                            st.pid, false);
+                    procMems.add(pssItem);
+
+                    ss[INDEX_NATIVE_PSS] += info.nativePss;
+                    ss[INDEX_NATIVE_SWAP_PSS] += info.nativeSwappedOutPss;
+                    ss[INDEX_NATIVE_RSS] += info.nativeRss;
+                    ss[INDEX_DALVIK_PSS] += info.dalvikPss;
+                    ss[INDEX_DALVIK_SWAP_PSS] += info.dalvikSwappedOutPss;
+                    ss[INDEX_DALVIK_RSS] += info.dalvikRss;
+                    for (int j = 0; j < dalvikSubitemPss.length; j++) {
+                        dalvikSubitemPss[j] += info.getOtherPss(
+                                Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        dalvikSubitemSwapPss[j] +=
+                                info.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        dalvikSubitemRss[j] += info.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
+                                + j);
+                    }
+                    ss[INDEX_OTHER_PSS] += info.otherPss;
+                    ss[INDEX_OTHER_SWAP_PSS] += info.otherSwappedOutPss;
+                    ss[INDEX_OTHER_RSS] += info.otherRss;
+                    for (int j = 0; j < Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+                        long mem = info.getOtherPss(j);
+                        miscPss[j] += mem;
+                        ss[INDEX_OTHER_PSS] -= mem;
+                        mem = info.getOtherSwappedOutPss(j);
+                        miscSwapPss[j] += mem;
+                        ss[INDEX_OTHER_SWAP_PSS] -= mem;
+                        mem = info.getOtherRss(j);
+                        miscRss[j] += mem;
+                        ss[INDEX_OTHER_RSS] -= mem;
+                    }
+                    oomPss[0] += myTotalPss;
+                    oomSwapPss[0] += myTotalSwapPss;
+                    if (oomProcs[0] == null) {
+                        oomProcs[0] = new ArrayList<MemItem>();
+                    }
+                    oomProcs[0].add(pssItem);
+                    oomRss[0] += myTotalRss;
                 }
-            }
+            });
 
             ArrayList<MemItem> catMems = new ArrayList<MemItem>();
 
-            catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, nativeRss, -1));
+            catMems.add(new MemItem("Native", "Native", ss[INDEX_NATIVE_PSS],
+                    ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
             final int dalvikId = -2;
-            catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikRss,
-                    dalvikId));
-            catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, otherRss, -3));
+            catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS],
+                    ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId));
+            catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS],
+                    ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3));
             for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                 String label = Debug.MemoryInfo.getOtherLabel(j);
                 catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j));
@@ -12636,7 +11732,7 @@
                         catMems, true, false, false);
             }
 
-            opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
+            opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && ss[INDEX_TOTAL_SWAP_PSS] != 0;
             if (!opts.oomOnly) {
                 dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_PROCESS, "proc",
                         procMems, true, true, opts.dumpSwapPss);
@@ -12649,29 +11745,30 @@
             }
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
-            if (nativeProcTotalPss > 0) {
+            if (ss[INDEX_TOTAL_NATIVE_PSS] > 0) {
                 synchronized (mProcessStats.mLock) {
                     final long cachedKb = memInfo.getCachedSizeKb();
                     final long freeKb = memInfo.getFreeSizeKb();
                     final long zramKb = memInfo.getZramTotalSizeKb();
                     final long kernelKb = memInfo.getKernelUsedSizeKb();
-                    EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
-                            kernelKb*1024, nativeProcTotalPss*1024);
+                    EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+                            kernelKb * 1024, ss[INDEX_TOTAL_NATIVE_PSS] * 1024);
                     mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
-                            nativeProcTotalPss);
+                            ss[INDEX_TOTAL_NATIVE_PSS]);
                 }
             }
             if (!brief) {
                 proto.write(MemInfoDumpProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
-                proto.write(MemInfoDumpProto.STATUS, mLastMemoryLevel);
+                proto.write(MemInfoDumpProto.STATUS, mAppProfiler.getLastMemoryLevelLocked());
                 proto.write(MemInfoDumpProto.CACHED_PSS_KB, cachedPss);
                 proto.write(MemInfoDumpProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
                 proto.write(MemInfoDumpProto.FREE_KB, memInfo.getFreeSizeKb());
             }
-            long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+            long lostRAM = memInfo.getTotalSizeKb()
+                    - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
                     - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
-            proto.write(MemInfoDumpProto.USED_PSS_KB, totalPss - cachedPss);
+            proto.write(MemInfoDumpProto.USED_PSS_KB, ss[INDEX_TOTAL_PSS] - cachedPss);
             proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
             proto.write(MemInfoDumpProto.LOST_RAM_KB, lostRAM);
             if (!brief) {
@@ -12742,13 +11839,8 @@
         updateCpuStatsNow();
         long[] memtrackTmp = new long[1];
         long[] swaptrackTmp = new long[2];
-        final List<ProcessCpuTracker.Stats> stats;
         // Get a list of Stats that have vsize > 0
-        synchronized (mProcessCpuTracker) {
-            stats = mProcessCpuTracker.getStats((st) -> {
-                return st.vsize > 0;
-            });
-        }
+        final List<ProcessCpuTracker.Stats> stats = mAppProfiler.getCpuStats(st -> st.vsize > 0);
         final int statsCount = stats.size();
         for (int i = 0; i < statsCount; i++) {
             ProcessCpuTracker.Stats st = stats.get(i);
@@ -13043,8 +12135,7 @@
         }
 
         mProcessesToGc.remove(app);
-        mPendingPssProcesses.remove(app);
-        ProcessList.abortNextPssTime(app.procStateMemTracker);
+        mAppProfiler.onCleanupApplicationRecordLocked(app);
 
         // Dismiss any open dialogs.
         app.getDialogController().clearAllErrorDialogs();
@@ -14582,10 +13673,7 @@
                                         timeFormatPreferenceMsgValue, 0);
                         mHandler.sendMessage(updateTimePreferenceMsg);
                     }
-                    BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
-                    synchronized (stats) {
-                        stats.noteCurrentTimeChangedLocked();
-                    }
+                    mBatteryStatsService.noteCurrentTimeChanged();
                     break;
                 case Intent.ACTION_CLEAR_DNS_CACHE:
                     mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
@@ -15385,8 +14473,8 @@
     }
 
     @Override
-    public StackInfo getFocusedStackInfo() throws RemoteException {
-        return mActivityTaskManager.getFocusedStackInfo();
+    public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
+        return mActivityTaskManager.getFocusedRootTaskInfo();
     }
 
     @Override
@@ -15598,229 +14686,6 @@
         }
     }
 
-    /** @hide */
-    public static Uri makeHeapDumpUri(String procName) {
-        return Uri.parse("content://com.android.shell.heapdump/" + procName + "_javaheap.bin");
-    }
-
-    private final class RecordPssRunnable implements Runnable {
-        private final ProcessRecord mProc;
-        private final Uri mDumpUri;
-        private final ContentResolver mContentResolver;
-
-        RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) {
-            mProc = proc;
-            mDumpUri = dumpUri;
-            mContentResolver = contentResolver;
-        }
-
-        @Override
-        public void run() {
-            try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) {
-                IApplicationThread thread = mProc.thread;
-                if (thread != null) {
-                    try {
-                        if (DEBUG_PSS) {
-                            Slog.d(TAG_PSS, "Requesting dump heap from "
-                                    + mProc + " to " + mDumpUri.getPath());
-                        }
-                        thread.dumpHeap(/* managed= */ true,
-                                /* mallocInfo= */ false, /* runGc= */ false,
-                                mDumpUri.getPath(), fd,
-                                /* finishCallback= */ null);
-                    } catch (RemoteException e) {
-                    }
-                }
-            } catch (IOException e) {
-                Slog.e(TAG, "Failed to dump heap", e);
-                // Need to clear the heap dump variables, otherwise no further heap dumps will be
-                // attempted.
-                abortHeapDump(mProc.processName);
-            }
-        }
-    }
-
-    /**
-     * Record new PSS sample for a process.
-     */
-    void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss,
-            long rss, int statType, long pssDuration, long now) {
-        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
-                swapPss * 1024, rss * 1024, statType, procState, pssDuration);
-        proc.lastPssTime = now;
-        synchronized (mProcessStats.mLock) {
-            proc.baseProcessTracker.addPss(
-                    pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
-        }
-        for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
-            ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
-                    proc.info.uid,
-                    holder.state.getName(),
-                    holder.state.getPackage(),
-                    pss, uss, rss, statType, pssDuration,
-                    holder.appVersion);
-        }
-        if (DEBUG_PSS) Slog.d(TAG_PSS,
-                "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
-                + " state=" + ProcessList.makeProcStateString(procState));
-        if (proc.initialIdlePss == 0) {
-            proc.initialIdlePss = pss;
-        }
-        proc.lastPss = pss;
-        proc.lastSwapPss = swapPss;
-        if (procState >= ActivityManager.PROCESS_STATE_HOME) {
-            proc.lastCachedPss = pss;
-            proc.lastCachedSwapPss = swapPss;
-        }
-        proc.mLastRss = rss;
-
-        final SparseArray<Pair<Long, String>> watchUids
-                = mMemWatchProcesses.getMap().get(proc.processName);
-        Long check = null;
-        if (watchUids != null) {
-            Pair<Long, String> val = watchUids.get(proc.uid);
-            if (val == null) {
-                val = watchUids.get(0);
-            }
-            if (val != null) {
-                check = val.first;
-            }
-        }
-        if (check != null) {
-            if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
-                boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
-                if (!isDebuggable) {
-                    if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                        isDebuggable = true;
-                    }
-                }
-                if (isDebuggable) {
-                    Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
-                    startHeapDumpLocked(proc, false);
-                } else {
-                    Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
-                            + ", but debugging not enabled");
-                }
-            }
-        }
-    }
-
-    private void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) {
-        mMemWatchDumpProcName = proc.processName;
-        mMemWatchDumpUri = makeHeapDumpUri(proc.processName);
-        mMemWatchDumpPid = proc.pid;
-        mMemWatchDumpUid = proc.uid;
-        mMemWatchIsUserInitiated = isUserInitiated;
-        Context ctx;
-        try {
-            ctx = mContext.createPackageContextAsUser("android", 0,
-                    UserHandle.getUserHandleForUid(mMemWatchDumpUid));
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException("android package not found.");
-        }
-        BackgroundThread.getHandler().post(
-                new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver()));
-    }
-
-    /**
-     * Schedule PSS collection of a process.
-     */
-    boolean requestPssLocked(ProcessRecord proc, int procState) {
-        if (mPendingPssProcesses.contains(proc)) {
-            return false;
-        }
-        if (mPendingPssProcesses.size() == 0) {
-            final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
-                    ? mPssDeferralTime : 0;
-            if (DEBUG_PSS && deferral > 0) {
-                Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
-                        + deferral + " ms");
-            }
-            mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, deferral);
-        }
-        if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
-        proc.pssProcState = procState;
-        proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
-        mPendingPssProcesses.add(proc);
-        return true;
-    }
-
-    /**
-     * Re-defer a posted PSS collection pass, if one exists.  Assumes deferral is
-     * currently active policy when called.
-     */
-    private void deferPssIfNeededLocked() {
-        if (mPendingPssProcesses.size() > 0) {
-            mBgHandler.removeMessages(COLLECT_PSS_BG_MSG);
-            mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, mPssDeferralTime);
-        }
-    }
-
-    private void deferPssForActivityStart() {
-        synchronized (ActivityManagerService.this) {
-            if (mPssDeferralTime > 0) {
-                if (DEBUG_PSS) {
-                    Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
-                }
-                deferPssIfNeededLocked();
-                mActivityStartingNesting.getAndIncrement();
-                mBgHandler.sendEmptyMessageDelayed(STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
-            }
-        }
-    }
-
-    /**
-     * Schedule PSS collection of all processes.
-     */
-    void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
-        if (!always) {
-            if (now < (mLastFullPssTime +
-                    (memLowered ? mConstants.FULL_PSS_LOWERED_INTERVAL
-                            : mConstants.FULL_PSS_MIN_INTERVAL))) {
-                return;
-            }
-        }
-        if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of all procs!  memLowered=" + memLowered);
-        mLastFullPssTime = now;
-        mFullPssPending = true;
-        for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
-            ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
-        }
-        mPendingPssProcesses.ensureCapacity(mProcessList.getLruSizeLocked());
-        mPendingPssProcesses.clear();
-        for (int i = mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
-            ProcessRecord app = mProcessList.mLruProcesses.get(i);
-            if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
-                continue;
-            }
-            if (memLowered || (always && now >
-                            app.lastStateTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
-                    || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
-                app.pssProcState = app.setProcState;
-                app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
-                        : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
-                app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
-                        app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
-                mPendingPssProcesses.add(app);
-            }
-        }
-        if (!mBgHandler.hasMessages(COLLECT_PSS_BG_MSG)) {
-            mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
-        }
-    }
-
-    public void setTestPssMode(boolean enabled) {
-        synchronized (this) {
-            mTestPssMode = enabled;
-            if (enabled) {
-                // Whenever we enable the mode, we want to take a snapshot all of current
-                // process mem use.
-                requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
-            }
-        }
-    }
-
     /**
      * Ask a given process to GC right now.
      */
@@ -15957,7 +14822,6 @@
         updateCpuStatsNow();
 
         synchronized (this) {
-            BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
             boolean doCpuKills = true;
             if (mLastPowerCheckUptime == 0) {
                 doCpuKills = false;
@@ -16004,10 +14868,8 @@
                             cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;
                         }
                         if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) {
-                            synchronized (stats) {
-                                stats.reportExcessiveCpuLocked(app.info.uid, app.processName,
+                            mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
                                         uptimeSince, cputimeUsed);
-                            }
                             app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
                                     + " dur=" + checkDur + " limit=" + cpuLimit,
                                     ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
@@ -16179,187 +15041,6 @@
     }
 
     @GuardedBy("this")
-    final boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
-        final int N = mProcessList.getLruSizeLocked();
-        final long now = SystemClock.uptimeMillis();
-        int memFactor;
-        if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
-            memFactor = mLowMemDetector.getMemFactor();
-        } else {
-            // Now determine the memory trimming level of background processes.
-            // Unfortunately we need to start at the back of the list to do this
-            // properly.  We only do this if the number of background apps we
-            // are managing to keep around is less than half the maximum we desire;
-            // if we are keeping a good number around, we'll let them use whatever
-            // memory they want.
-            if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
-                && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
-                final int numCachedAndEmpty = numCached + numEmpty;
-                if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
-                    memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
-                } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
-                    memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
-                } else {
-                    memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
-                }
-            } else {
-                memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
-            }
-        }
-        // We always allow the memory level to go up (better).  We only allow it to go
-        // down if we are in a state where that is allowed, *and* the total number of processes
-        // has gone down since last time.
-        if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
-                + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
-                + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
-        if (memFactor > mLastMemoryLevel) {
-            if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
-                memFactor = mLastMemoryLevel;
-                if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
-            }
-        }
-        if (memFactor != mLastMemoryLevel) {
-            EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
-            FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
-        }
-        mLastMemoryLevel = memFactor;
-        mLastNumProcesses = mProcessList.getLruSizeLocked();
-        boolean allChanged;
-        int trackerMemFactor;
-        synchronized (mProcessStats.mLock) {
-            allChanged = mProcessStats.setMemFactorLocked(
-                    memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
-            trackerMemFactor = mProcessStats.getMemFactorLocked();
-        }
-        if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
-            if (mLowRamStartTime == 0) {
-                mLowRamStartTime = now;
-            }
-            int step = 0;
-            int fgTrimLevel;
-            switch (memFactor) {
-                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
-                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
-                    break;
-                case ProcessStats.ADJ_MEM_FACTOR_LOW:
-                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
-                    break;
-                default:
-                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
-                    break;
-            }
-            int factor = numTrimming/3;
-            int minFactor = 2;
-            if (mAtmInternal.getHomeProcess() != null) minFactor++;
-            if (mAtmInternal.getPreviousProcess() != null) minFactor++;
-            if (factor < minFactor) factor = minFactor;
-            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-            for (int i=N-1; i>=0; i--) {
-                ProcessRecord app = mProcessList.mLruProcesses.get(i);
-                if (allChanged || app.procStateChanged) {
-                    setProcessTrackerStateLocked(app, trackerMemFactor, now);
-                    app.procStateChanged = false;
-                }
-                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
-                        && !app.killedByAm) {
-                    if (app.trimMemoryLevel < curLevel && app.thread != null) {
-                        try {
-                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
-                                    "Trimming memory of " + app.processName + " to " + curLevel);
-                            app.thread.scheduleTrimMemory(curLevel);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    app.trimMemoryLevel = curLevel;
-                    step++;
-                    if (step >= factor) {
-                        step = 0;
-                        switch (curLevel) {
-                            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
-                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
-                                break;
-                            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
-                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
-                                break;
-                        }
-                    }
-                } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
-                        && !app.killedByAm) {
-                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
-                            && app.thread != null) {
-                        try {
-                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
-                                    "Trimming memory of heavy-weight " + app.processName
-                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
-                            app.thread.scheduleTrimMemory(
-                                    ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
-                } else {
-                    if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                            || app.systemNoUi) && app.hasPendingUiClean()) {
-                        // If this application is now in the background and it
-                        // had done UI, then give it the special trim level to
-                        // have it free UI resources.
-                        final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
-                        if (app.trimMemoryLevel < level && app.thread != null) {
-                            try {
-                                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
-                                        "Trimming memory of bg-ui " + app.processName
-                                        + " to " + level);
-                                app.thread.scheduleTrimMemory(level);
-                            } catch (RemoteException e) {
-                            }
-                        }
-                        app.setPendingUiClean(false);
-                    }
-                    if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
-                        try {
-                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
-                                    "Trimming memory of fg " + app.processName
-                                    + " to " + fgTrimLevel);
-                            app.thread.scheduleTrimMemory(fgTrimLevel);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    app.trimMemoryLevel = fgTrimLevel;
-                }
-            }
-        } else {
-            if (mLowRamStartTime != 0) {
-                mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
-                mLowRamStartTime = 0;
-            }
-            for (int i=N-1; i>=0; i--) {
-                ProcessRecord app = mProcessList.mLruProcesses.get(i);
-                if (allChanged || app.procStateChanged) {
-                    setProcessTrackerStateLocked(app, trackerMemFactor, now);
-                    app.procStateChanged = false;
-                }
-                if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                        || app.systemNoUi) && app.hasPendingUiClean()) {
-                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
-                            && app.thread != null) {
-                        try {
-                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
-                                    "Trimming memory of ui hidden " + app.processName
-                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
-                            app.thread.scheduleTrimMemory(
-                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    app.setPendingUiClean(false);
-                }
-                app.trimMemoryLevel = 0;
-            }
-        }
-        return allChanged;
-    }
-
-    @GuardedBy("this")
     final void updateOomAdjLocked(String oomAdjReason) {
         mOomAdjuster.updateOomAdjLocked(oomAdjReason);
     }
@@ -16647,109 +15328,31 @@
         }
     }
 
-    private void stopProfilerLocked(ProcessRecord proc, int profileType) {
-        if (proc == null || proc == mProfileData.getProfileProc()) {
-            proc = mProfileData.getProfileProc();
-            profileType = mProfileType;
-            clearProfilerLocked();
-        }
-        if (proc == null) {
-            return;
-        }
-        try {
-            proc.thread.profilerControl(false, null, profileType);
-        } catch (RemoteException e) {
-            throw new IllegalStateException("Process disappeared");
-        }
-    }
-
-    void clearProfilerLocked() {
-        if (mProfileData.getProfilerInfo() != null
-                && mProfileData.getProfilerInfo().profileFd != null) {
-            try {
-                mProfileData.getProfilerInfo().profileFd.close();
-            } catch (IOException e) {
-            }
-        }
-        mProfileData.setProfileApp(null);
-        mProfileData.setProfileProc(null);
-        mProfileData.setProfilerInfo(null);
-    }
-
     public boolean profileControl(String process, int userId, boolean start,
             ProfilerInfo profilerInfo, int profileType) throws RemoteException {
-
-        try {
-            synchronized (this) {
-                // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
-                // its own permission.
-                if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
-                        != PackageManager.PERMISSION_GRANTED) {
-                    throw new SecurityException("Requires permission "
-                            + android.Manifest.permission.SET_ACTIVITY_WATCHER);
-                }
-
-                if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
-                    throw new IllegalArgumentException("null profile info or fd");
-                }
-
-                ProcessRecord proc = null;
-                if (process != null) {
-                    proc = findProcessLocked(process, userId, "profileControl");
-                }
-
-                if (start && (proc == null || proc.thread == null)) {
-                    throw new IllegalArgumentException("Unknown process: " + process);
-                }
-
-                if (start) {
-                    stopProfilerLocked(null, 0);
-                    setProfileApp(proc.info, proc.processName, profilerInfo);
-                    mProfileData.setProfileProc(proc);
-                    mProfileType = profileType;
-                    ParcelFileDescriptor fd = profilerInfo.profileFd;
-                    try {
-                        fd = fd.dup();
-                    } catch (IOException e) {
-                        fd = null;
-                    }
-                    profilerInfo.profileFd = fd;
-                    proc.thread.profilerControl(start, profilerInfo, profileType);
-                    fd = null;
-                    try {
-                        mProfileData.getProfilerInfo().profileFd.close();
-                    } catch (IOException e) {
-                    }
-                    mProfileData.getProfilerInfo().profileFd = null;
-
-                    if (proc.pid == MY_PID) {
-                        // When profiling the system server itself, avoid closing the file
-                        // descriptor, as profilerControl will not create a copy.
-                        // Note: it is also not correct to just set profileFd to null, as the
-                        //       whole ProfilerInfo instance is passed down!
-                        profilerInfo = null;
-                    }
-                } else {
-                    stopProfilerLocked(proc, profileType);
-                    if (profilerInfo != null && profilerInfo.profileFd != null) {
-                        try {
-                            profilerInfo.profileFd.close();
-                        } catch (IOException e) {
-                        }
-                    }
-                }
-
-                return true;
+        synchronized (this) {
+            // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+            // its own permission.
+            if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires permission "
+                        + android.Manifest.permission.SET_ACTIVITY_WATCHER);
             }
-        } catch (RemoteException e) {
-            throw new IllegalStateException("Process disappeared");
-        } finally {
-            if (profilerInfo != null && profilerInfo.profileFd != null) {
-                try {
-                    profilerInfo.profileFd.close();
-                } catch (IOException e) {
-                }
+
+            if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
+                throw new IllegalArgumentException("null profile info or fd");
             }
+
+            ProcessRecord proc = null;
+            if (process != null) {
+                proc = findProcessLocked(process, userId, "profileControl");
+            }
+
+            if (start && (proc == null || proc.thread == null)) {
+                throw new IllegalArgumentException("Unknown process: " + process);
+            }
+
+            return mAppProfiler.profileControlLocked(proc, start, profilerInfo, profileType);
         }
     }
 
@@ -16809,7 +15412,7 @@
                     throw new IllegalArgumentException("Unknown process: " + process);
                 }
 
-                boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+                boolean isDebuggable = Build.IS_DEBUGGABLE;
                 if (!isDebuggable) {
                     if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
                         throw new SecurityException("Process not debuggable: " + proc);
@@ -16868,45 +15471,12 @@
                 }
             }
         }
-        synchronized (this) {
-            if (maxMemSize > 0) {
-                mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
-            } else {
-                if (uid != 0) {
-                    mMemWatchProcesses.remove(processName, uid);
-                } else {
-                    mMemWatchProcesses.getMap().remove(processName);
-                }
-            }
-        }
+        mAppProfiler.setDumpHeapDebugLimit(processName, uid, maxMemSize, reportPackage);
     }
 
     @Override
     public void dumpHeapFinished(String path) {
-        synchronized (this) {
-            if (Binder.getCallingPid() != mMemWatchDumpPid) {
-                Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid()
-                        + " does not match last pid " + mMemWatchDumpPid);
-                return;
-            }
-            if (mMemWatchDumpUri == null || !mMemWatchDumpUri.getPath().equals(path)) {
-                Slog.w(TAG, "dumpHeapFinished: Calling path " + path
-                        + " does not match last path " + mMemWatchDumpUri);
-                return;
-            }
-            if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
-            mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
-
-            // Forced gc to clean up the remnant hprof fd.
-            Runtime.getRuntime().gc();
-        }
-    }
-
-    /** Clear the currently executing heap dump variables so a new heap dump can be started. */
-    private void abortHeapDump(String procName) {
-        Message msg = mHandler.obtainMessage(ABORT_DUMPHEAP_MSG);
-        msg.obj = procName;
-        mHandler.sendMessage(msg);
+        mAppProfiler.dumpHeapFinished(path, Binder.getCallingPid());
     }
 
     /** In this method we try to acquire our lock to make sure that we have not deadlocked */
@@ -17058,8 +15628,7 @@
             return false;
         }
 
-        boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
-        if (!isDebuggable) {
+        if (!Build.IS_DEBUGGABLE) {
             if ((process.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
                 return false;
             }
@@ -17489,7 +16058,7 @@
         @Override
         public void updateCpuStats() {
             synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.updateCpuStats();
+                ActivityManagerService.this.updateCpuStatsLocked();
             }
         }
 
@@ -17513,19 +16082,7 @@
         @Override
         public void updateForegroundTimeIfOnBattery(
                 String packageName, int uid, long cpuTimeDiff) {
-            synchronized (ActivityManagerService.this) {
-                if (!mBatteryStatsService.isOnBattery()) {
-                    return;
-                }
-                final BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
-                synchronized (bsi) {
-                    final BatteryStatsImpl.Uid.Proc ps =
-                            bsi.getProcessStatsLocked(uid, packageName);
-                    if (ps != null) {
-                        ps.addForegroundTimeLocked(cpuTimeDiff);
-                    }
-                }
-            }
+            mBatteryStatsService.updateForegroundTimeIfOnBattery(packageName, uid, cpuTimeDiff);
         }
 
         @Override
@@ -18420,8 +16977,7 @@
                     throw new IllegalArgumentException("Unknown process: " + process);
                 }
 
-                boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
-                if (!isDebuggable) {
+                if (!Build.IS_DEBUGGABLE) {
                     if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
                         throw new SecurityException("Process not debuggable: " + proc);
                     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a512cca..9ce8b11 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -28,6 +28,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.AppGlobals;
 import android.app.BroadcastOptions;
 import android.app.IActivityController;
@@ -2586,7 +2587,7 @@
             case "list":
                 return runStackList(pw);
             case "info":
-                return runStackInfo(pw);
+                return runRootTaskInfo(pw);
             case "move-top-activity-to-pinned-stack":
                 return runMoveTopActivityToPinnedStack(pw);
             case "remove":
@@ -2668,17 +2669,17 @@
     }
 
     int runStackList(PrintWriter pw) throws RemoteException {
-        List<ActivityManager.StackInfo> stacks = mTaskInterface.getAllStackInfos();
-        for (ActivityManager.StackInfo info : stacks) {
+        List<RootTaskInfo> tasks = mTaskInterface.getAllRootTaskInfos();
+        for (RootTaskInfo info : tasks) {
             pw.println(info);
         }
         return 0;
     }
 
-    int runStackInfo(PrintWriter pw) throws RemoteException {
+    int runRootTaskInfo(PrintWriter pw) throws RemoteException {
         int windowingMode = Integer.parseInt(getNextArgRequired());
         int activityType = Integer.parseInt(getNextArgRequired());
-        ActivityManager.StackInfo info = mTaskInterface.getStackInfo(windowingMode, activityType);
+        RootTaskInfo info = mTaskInterface.getRootTaskInfo(windowingMode, activityType);
         pw.println(info);
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 5d429d3..3f55bff 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -22,7 +22,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
@@ -36,10 +35,10 @@
 import android.content.pm.VersionedPackage;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Message;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArrayMap;
@@ -596,7 +595,7 @@
 
         return mService.mAtmInternal.handleAppCrashInActivityController(
                 name, pid, shortMsg, longMsg, timeMillis, crashInfo.stackTrace, () -> {
-                if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
+                if (Build.IS_DEBUGGABLE
                         && "Native crash".equals(crashInfo.exceptionClassName)) {
                     Slog.w(TAG, "Skip killing native crashed app " + name
                             + "(" + pid + ") during testing");
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
new file mode 100644
index 0000000..0b5d585
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -0,0 +1,1731 @@
+/*
+ * 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.server.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+
+import android.annotation.BroadcastBehavior;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
+import android.content.ComponentCallbacks2;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.MemInfoReader;
+import com.android.server.am.ProcessList.ProcStateMemTracker;
+import com.android.server.utils.PriorityDump;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * A helper class taking care of the profiling, memory and cpu sampling of apps
+ */
+public class AppProfiler {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+
+    static final String TAG_PSS = TAG + POSTFIX_PSS;
+
+    static final String TAG_OOM_ADJ = ActivityManagerService.TAG_OOM_ADJ;
+
+    /** Control over CPU and battery monitoring */
+    // write battery stats every 30 minutes.
+    static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
+
+    static final boolean MONITOR_CPU_USAGE = true;
+
+    // don't sample cpu less than every 5 seconds.
+    static final long MONITOR_CPU_MIN_TIME = 5 * 1000;
+
+    // wait possibly forever for next cpu sample.
+    static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;
+
+    static final boolean MONITOR_THREAD_CPU_USAGE = false;
+
+    static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
+
+    /**
+     * Broadcast sent when heap dump collection has been completed.
+     */
+    @BroadcastBehavior(includeBackground = true, protectedBroadcast = true)
+    private static final String ACTION_HEAP_DUMP_FINISHED =
+            "com.android.internal.intent.action.HEAP_DUMP_FINISHED";
+
+    /**
+     * The process we are reporting
+     */
+    private static final String EXTRA_HEAP_DUMP_PROCESS_NAME =
+            "com.android.internal.extra.heap_dump.PROCESS_NAME";
+
+    /**
+     * The size limit the process reached.
+     */
+    private static final String EXTRA_HEAP_DUMP_SIZE_BYTES =
+            "com.android.internal.extra.heap_dump.SIZE_BYTES";
+
+    /**
+     * Whether the user initiated the dump or not.
+     */
+    private static final String EXTRA_HEAP_DUMP_IS_USER_INITIATED =
+            "com.android.internal.extra.heap_dump.IS_USER_INITIATED";
+
+    /**
+     * Optional name of package to directly launch.
+     */
+    private static final String EXTRA_HEAP_DUMP_REPORT_PACKAGE =
+            "com.android.internal.extra.heap_dump.REPORT_PACKAGE";
+
+    /**
+     * How long we defer PSS gathering while activities are starting, in milliseconds.
+     * This is adjustable via DeviceConfig.  If it is zero or negative, no PSS deferral
+     * is done.
+     */
+    private volatile long mPssDeferralTime = 0;
+
+    /**
+     * Processes we want to collect PSS data from.
+     */
+    @GuardedBy("mService")
+    private final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
+
+    /**
+     * Depth of overlapping activity-start PSS deferral notes
+     */
+    private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
+
+    /**
+     * Last time we requested PSS data of all processes.
+     */
+    @GuardedBy("mService")
+    private long mLastFullPssTime = SystemClock.uptimeMillis();
+
+    /**
+     * If set, the next time we collect PSS data we should do a full collection
+     * with data from native processes and the kernel.
+     */
+    @GuardedBy("mService")
+    private boolean mFullPssPending = false;
+
+    /**
+     * If true, we are running under a test environment so will sample PSS from processes
+     * much more rapidly to try to collect better data when the tests are rapidly
+     * running through apps.
+     */
+    @GuardedBy("mService")
+    private boolean mTestPssMode = false;
+
+    @GuardedBy("mService")
+    private final LowMemDetector mLowMemDetector;
+
+    /**
+     * Allow the current computed overall memory level of the system to go down?
+     * This is set to false when we are killing processes for reasons other than
+     * memory management, so that the now smaller process list will not be taken as
+     * an indication that memory is tighter.
+     */
+    @GuardedBy("mService")
+    private boolean mAllowLowerMemLevel = false;
+
+    /**
+     * The last computed memory level, for holding when we are in a state that
+     * processes are going away for other reasons.
+     */
+    @GuardedBy("mService")
+    private int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+
+    /**
+     * The last total number of process we have, to determine if changes actually look
+     * like a shrinking number of process due to lower RAM.
+     */
+    @GuardedBy("mService")
+    private int mLastNumProcesses;
+
+    /**
+     * Total time spent with RAM that has been added in the past since the last idle time.
+     */
+    @GuardedBy("mService")
+    private long mLowRamTimeSinceLastIdle = 0;
+
+    /**
+     * If RAM is currently low, when that horrible situation started.
+     */
+    @GuardedBy("mService")
+    private long mLowRamStartTime = 0;
+
+    /**
+     * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+     * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+     * A non-null agent in mProfileInfo overrides this.
+     */
+    private @Nullable Map<String, String> mAppAgentMap = null;
+
+    private int mProfileType = 0;
+    private final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
+    private String mMemWatchDumpProcName;
+    private Uri mMemWatchDumpUri;
+    private int mMemWatchDumpPid;
+    private int mMemWatchDumpUid;
+    private boolean mMemWatchIsUserInitiated;
+
+    /**
+     * Used to collect per-process CPU use for ANRs, battery stats, etc.
+     * Must acquire this object's lock when accessing it.
+     * NOTE: this lock will be held while doing long operations (trawling
+     * through all processes in /proc), so it should never be acquired by
+     * any critical paths such as when holding the main activity manager lock.
+     */
+    private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
+            MONITOR_THREAD_CPU_USAGE);
+    private final AtomicLong mLastCpuTime = new AtomicLong(0);
+    private final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
+    private final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
+
+    private long mLastWriteTime = 0;
+    private final ProfileData mProfileData = new ProfileData();
+
+    /**
+     * Runtime CPU use collection thread.  This object's lock is used to
+     * perform synchronization with the thread (notifying it to run).
+     */
+    private final Thread mProcessCpuThread;
+
+    private final ActivityManagerService mService;
+    private final Handler mBgHandler;
+
+    /**
+     * Observe DeviceConfig changes to the PSS calculation interval
+     */
+    private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+                @Override
+                public void onPropertiesChanged(Properties properties) {
+                    if (properties.getKeyset().contains(ACTIVITY_START_PSS_DEFER_CONFIG)) {
+                        mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
+                        if (DEBUG_PSS) {
+                            Slog.d(TAG_PSS, "Activity-start PSS delay now "
+                                    + mPssDeferralTime + " ms");
+                        }
+                    }
+                }
+            };
+
+    private class ProfileData {
+        private String mProfileApp = null;
+        private ProcessRecord mProfileProc = null;
+        private ProfilerInfo mProfilerInfo = null;
+
+        void setProfileApp(String profileApp) {
+            mProfileApp = profileApp;
+            if (mService.mAtmInternal != null) {
+                mService.mAtmInternal.setProfileApp(profileApp);
+            }
+        }
+
+        String getProfileApp() {
+            return mProfileApp;
+        }
+
+        void setProfileProc(ProcessRecord profileProc) {
+            mProfileProc = profileProc;
+            if (mService.mAtmInternal != null) {
+                mService.mAtmInternal.setProfileProc(profileProc == null ? null
+                        : profileProc.getWindowProcessController());
+            }
+        }
+
+        ProcessRecord getProfileProc() {
+            return mProfileProc;
+        }
+
+        void setProfilerInfo(ProfilerInfo profilerInfo) {
+            mProfilerInfo = profilerInfo;
+            if (mService.mAtmInternal != null) {
+                mService.mAtmInternal.setProfilerInfo(profilerInfo);
+            }
+        }
+
+        ProfilerInfo getProfilerInfo() {
+            return mProfilerInfo;
+        }
+    }
+
+    private class BgHandler extends Handler {
+        static final int COLLECT_PSS_BG_MSG = 1;
+        static final int DEFER_PSS_MSG = 2;
+        static final int STOP_DEFERRING_PSS_MSG = 3;
+        BgHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case COLLECT_PSS_BG_MSG:
+                    collectPssInBackground();
+                    break;
+                case DEFER_PSS_MSG:
+                    deferPssForActivityStart();
+                    break;
+                case STOP_DEFERRING_PSS_MSG:
+                    stopDeferPss();
+                    break;
+            }
+        }
+    }
+
+    private void collectPssInBackground() {
+        long start = SystemClock.uptimeMillis();
+        MemInfoReader memInfo = null;
+        synchronized (mService) {
+            if (mFullPssPending) {
+                mFullPssPending = false;
+                memInfo = new MemInfoReader();
+            }
+        }
+        if (memInfo != null) {
+            updateCpuStatsNow();
+            long nativeTotalPss = 0;
+            final List<ProcessCpuTracker.Stats> stats;
+            synchronized (mProcessCpuTracker) {
+                stats = mProcessCpuTracker.getStats(st -> {
+                    return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
+                });
+            }
+            final int numOfStats = stats.size();
+            for (int j = 0; j < numOfStats; j++) {
+                synchronized (mService.mPidsSelfLocked) {
+                    if (mService.mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
+                        // This is one of our own processes; skip it.
+                        continue;
+                    }
+                }
+                nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
+            }
+            memInfo.readMemInfo();
+            synchronized (mService.mProcessStats.mLock) {
+                if (DEBUG_PSS) {
+                    Slog.d(TAG_PSS, "Collected native and kernel memory in "
+                            + (SystemClock.uptimeMillis() - start) + "ms");
+                }
+                final long cachedKb = memInfo.getCachedSizeKb();
+                final long freeKb = memInfo.getFreeSizeKb();
+                final long zramKb = memInfo.getZramTotalSizeKb();
+                final long kernelKb = memInfo.getKernelUsedSizeKb();
+                EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
+                        kernelKb * 1024, nativeTotalPss * 1024);
+                mService.mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+                        nativeTotalPss);
+            }
+        }
+
+        int num = 0;
+        long[] tmp = new long[3];
+        do {
+            ProcessRecord proc;
+            int procState;
+            int statType;
+            int pid = -1;
+            long lastPssTime;
+            synchronized (mService) {
+                if (mPendingPssProcesses.size() <= 0) {
+                    if (mTestPssMode || DEBUG_PSS) {
+                        Slog.d(TAG_PSS,
+                                "Collected pss of " + num + " processes in "
+                                + (SystemClock.uptimeMillis() - start) + "ms");
+                    }
+                    mPendingPssProcesses.clear();
+                    return;
+                }
+                proc = mPendingPssProcesses.remove(0);
+                procState = proc.pssProcState;
+                statType = proc.pssStatType;
+                lastPssTime = proc.lastPssTime;
+                long now = SystemClock.uptimeMillis();
+                if (proc.thread != null && procState == proc.setProcState
+                        && (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) < now) {
+                    pid = proc.pid;
+                } else {
+                    abortNextPssTime(proc.procStateMemTracker);
+                    if (DEBUG_PSS) {
+                        Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+                                + ": still need "
+                                + (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE - now)
+                                + "ms until safe");
+                    }
+                    proc = null;
+                    pid = 0;
+                }
+            }
+            if (proc != null) {
+                long startTime = SystemClock.currentThreadTimeMillis();
+                // skip background PSS calculation of apps that are capturing
+                // camera imagery
+                final boolean usingCamera = mService.isCameraActiveForUid(proc.uid);
+                long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
+                long endTime = SystemClock.currentThreadTimeMillis();
+                synchronized (mService) {
+                    if (pss != 0 && proc.thread != null && proc.setProcState == procState
+                            && proc.pid == pid && proc.lastPssTime == lastPssTime) {
+                        num++;
+                        commitNextPssTime(proc.procStateMemTracker);
+                        recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
+                                statType, endTime - startTime, SystemClock.uptimeMillis());
+                    } else {
+                        abortNextPssTime(proc.procStateMemTracker);
+                        if (DEBUG_PSS) {
+                            Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+                                    + ": " + (proc.thread == null ? "NO_THREAD " : "")
+                                    + (usingCamera ? "CAMERA " : "")
+                                    + (proc.pid != pid ? "PID_CHANGED " : "")
+                                    + " initState=" + procState + " curState="
+                                    + proc.setProcState + " "
+                                    + (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
+                        }
+                    }
+                }
+            }
+        } while (true);
+    }
+
+    private static void commitNextPssTime(ProcStateMemTracker tracker) {
+        if (tracker.mPendingMemState >= 0) {
+            tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
+            tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
+            tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
+            tracker.mPendingMemState = -1;
+        }
+    }
+
+    private static void abortNextPssTime(ProcStateMemTracker tracker) {
+        tracker.mPendingMemState = -1;
+    }
+
+    @GuardedBy("mService")
+    void updateNextPssTimeLocked(int procState, ProcessRecord app, long now, boolean forceUpdate) {
+        if (!forceUpdate) {
+            if (now <= app.nextPssTime
+                    && now <= Math.max(app.lastPssTime + ProcessList.PSS_MAX_INTERVAL,
+                    app.lastStateTime + ProcessList.minTimeFromStateChange(mTestPssMode))) {
+                // update is not due, ignore it.
+                return;
+            }
+            if (!requestPssLocked(app, app.setProcState)) {
+                return;
+            }
+        }
+        app.nextPssTime = ProcessList.computeNextPssTime(procState, app.procStateMemTracker,
+                mTestPssMode, mService.mAtmInternal.isSleeping(), now);
+    }
+
+    /**
+     * Record new PSS sample for a process.
+     */
+    @GuardedBy("mService")
+    private void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss,
+            long swapPss, long rss, int statType, long pssDuration, long now) {
+        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
+                swapPss * 1024, rss * 1024, statType, procState, pssDuration);
+        proc.lastPssTime = now;
+        synchronized (mService.mProcessStats.mLock) {
+            proc.baseProcessTracker.addPss(
+                    pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
+        }
+        for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
+            ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
+                    proc.info.uid,
+                    holder.state.getName(),
+                    holder.state.getPackage(),
+                    pss, uss, rss, statType, pssDuration,
+                    holder.appVersion);
+        }
+        if (DEBUG_PSS) {
+            Slog.d(TAG_PSS,
+                    "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+                    + " state=" + ProcessList.makeProcStateString(procState));
+        }
+        if (proc.initialIdlePss == 0) {
+            proc.initialIdlePss = pss;
+        }
+        proc.lastPss = pss;
+        proc.lastSwapPss = swapPss;
+        if (procState >= ActivityManager.PROCESS_STATE_HOME) {
+            proc.lastCachedPss = pss;
+            proc.lastCachedSwapPss = swapPss;
+        }
+        proc.mLastRss = rss;
+
+        final SparseArray<Pair<Long, String>> watchUids =
+                mMemWatchProcesses.getMap().get(proc.processName);
+        Long check = null;
+        if (watchUids != null) {
+            Pair<Long, String> val = watchUids.get(proc.uid);
+            if (val == null) {
+                val = watchUids.get(0);
+            }
+            if (val != null) {
+                check = val.first;
+            }
+        }
+        if (check != null) {
+            if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
+                boolean isDebuggable = Build.IS_DEBUGGABLE;
+                if (!isDebuggable) {
+                    if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                        isDebuggable = true;
+                    }
+                }
+                if (isDebuggable) {
+                    Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
+                    startHeapDumpLocked(proc, false);
+                } else {
+                    Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
+                            + ", but debugging not enabled");
+                }
+            }
+        }
+    }
+
+    private final class RecordPssRunnable implements Runnable {
+        private final ProcessRecord mProc;
+        private final Uri mDumpUri;
+        private final ContentResolver mContentResolver;
+
+        RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) {
+            mProc = proc;
+            mDumpUri = dumpUri;
+            mContentResolver = contentResolver;
+        }
+
+        @Override
+        public void run() {
+            try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) {
+                IApplicationThread thread = mProc.thread;
+                if (thread != null) {
+                    try {
+                        if (DEBUG_PSS) {
+                            Slog.d(TAG_PSS, "Requesting dump heap from "
+                                    + mProc + " to " + mDumpUri.getPath());
+                        }
+                        thread.dumpHeap(/* managed= */ true,
+                                /* mallocInfo= */ false, /* runGc= */ false,
+                                mDumpUri.getPath(), fd,
+                                /* finishCallback= */ null);
+                    } catch (RemoteException e) {
+                    }
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to dump heap", e);
+                // Need to clear the heap dump variables, otherwise no further heap dumps will be
+                // attempted.
+                abortHeapDump(mProc.processName);
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) {
+        mMemWatchDumpProcName = proc.processName;
+        mMemWatchDumpUri = makeHeapDumpUri(proc.processName);
+        mMemWatchDumpPid = proc.pid;
+        mMemWatchDumpUid = proc.uid;
+        mMemWatchIsUserInitiated = isUserInitiated;
+        Context ctx;
+        try {
+            ctx = mService.mContext.createPackageContextAsUser("android", 0,
+                    UserHandle.getUserHandleForUid(mMemWatchDumpUid));
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException("android package not found.");
+        }
+        BackgroundThread.getHandler().post(
+                new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver()));
+    }
+
+    void dumpHeapFinished(String path, int callerPid) {
+        synchronized (mService) {
+            if (callerPid != mMemWatchDumpPid) {
+                Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid()
+                        + " does not match last pid " + mMemWatchDumpPid);
+                return;
+            }
+            if (mMemWatchDumpUri == null || !mMemWatchDumpUri.getPath().equals(path)) {
+                Slog.w(TAG, "dumpHeapFinished: Calling path " + path
+                        + " does not match last path " + mMemWatchDumpUri);
+                return;
+            }
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
+            mService.mHandler.sendEmptyMessage(
+                    ActivityManagerService.POST_DUMP_HEAP_NOTIFICATION_MSG);
+
+            // Forced gc to clean up the remnant hprof fd.
+            Runtime.getRuntime().gc();
+        }
+    }
+
+    void handlePostDumpHeapNotification() {
+        final String procName;
+        final int uid;
+        final long memLimit;
+        final String reportPackage;
+        final boolean isUserInitiated;
+        synchronized (mService) {
+            uid = mMemWatchDumpUid;
+            procName = mMemWatchDumpProcName;
+            Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
+            if (val == null) {
+                val = mMemWatchProcesses.get(procName, 0);
+            }
+            if (val != null) {
+                memLimit = val.first;
+                reportPackage = val.second;
+            } else {
+                memLimit = 0;
+                reportPackage = null;
+            }
+            isUserInitiated = mMemWatchIsUserInitiated;
+
+            mMemWatchDumpUri = null;
+            mMemWatchDumpProcName = null;
+            mMemWatchDumpPid = -1;
+            mMemWatchDumpUid = -1;
+        }
+        if (procName == null) {
+            return;
+        }
+
+        if (DEBUG_PSS) {
+            Slog.d(TAG_PSS, "Showing dump heap notification from " + procName + "/" + uid);
+        }
+
+        Intent dumpFinishedIntent = new Intent(ACTION_HEAP_DUMP_FINISHED);
+        // Send this only to the Shell package.
+        dumpFinishedIntent.setPackage("com.android.shell");
+        dumpFinishedIntent.putExtra(Intent.EXTRA_UID, uid);
+        dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_IS_USER_INITIATED, isUserInitiated);
+        dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_SIZE_BYTES, memLimit);
+        dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_REPORT_PACKAGE, reportPackage);
+        dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_PROCESS_NAME, procName);
+
+        mService.mContext.sendBroadcastAsUser(dumpFinishedIntent,
+                UserHandle.getUserHandleForUid(uid));
+    }
+
+    void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+            String reportPackage) {
+        synchronized (mService) {
+            if (maxMemSize > 0) {
+                mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
+            } else {
+                if (uid != 0) {
+                    mMemWatchProcesses.remove(processName, uid);
+                } else {
+                    mMemWatchProcesses.getMap().remove(processName);
+                }
+            }
+        }
+    }
+
+    /** Clear the currently executing heap dump variables so a new heap dump can be started. */
+    private void abortHeapDump(String procName) {
+        Message msg = mService.mHandler.obtainMessage(ActivityManagerService.ABORT_DUMPHEAP_MSG);
+        msg.obj = procName;
+        mService.mHandler.sendMessage(msg);
+    }
+
+    void handleAbortDumpHeap(String procName) {
+        if (procName != null) {
+            synchronized (mService) {
+                if (procName.equals(mMemWatchDumpProcName)) {
+                    mMemWatchDumpProcName = null;
+                    mMemWatchDumpUri = null;
+                    mMemWatchDumpPid = -1;
+                    mMemWatchDumpUid = -1;
+                }
+            }
+        }
+    }
+
+    /** @hide */
+    private static Uri makeHeapDumpUri(String procName) {
+        return Uri.parse("content://com.android.shell.heapdump/" + procName + "_javaheap.bin");
+    }
+
+    /**
+     * Schedule PSS collection of a process.
+     */
+    @GuardedBy("mService")
+    private boolean requestPssLocked(ProcessRecord proc, int procState) {
+        if (mPendingPssProcesses.contains(proc)) {
+            return false;
+        }
+        if (mPendingPssProcesses.size() == 0) {
+            final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
+                    ? mPssDeferralTime : 0;
+            if (DEBUG_PSS && deferral > 0) {
+                Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+                        + deferral + " ms");
+            }
+            mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, deferral);
+        }
+        if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
+        proc.pssProcState = procState;
+        proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
+        mPendingPssProcesses.add(proc);
+        return true;
+    }
+
+    /**
+     * Re-defer a posted PSS collection pass, if one exists.  Assumes deferral is
+     * currently active policy when called.
+     */
+    @GuardedBy("mService")
+    private void deferPssIfNeededLocked() {
+        if (mPendingPssProcesses.size() > 0) {
+            mBgHandler.removeMessages(BgHandler.COLLECT_PSS_BG_MSG);
+            mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, mPssDeferralTime);
+        }
+    }
+
+    private void deferPssForActivityStart() {
+        if (mPssDeferralTime > 0) {
+            if (DEBUG_PSS) {
+                Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
+            }
+            deferPssIfNeededLocked();
+            mActivityStartingNesting.getAndIncrement();
+            mBgHandler.sendEmptyMessageDelayed(BgHandler.STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
+        }
+    }
+
+    private void stopDeferPss() {
+        final int nesting = mActivityStartingNesting.decrementAndGet();
+        if (nesting <= 0) {
+            if (DEBUG_PSS) {
+                Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
+                        + nesting);
+            }
+            if (nesting < 0) {
+                Slog.wtf(TAG, "Activity start nesting undercount!");
+                mActivityStartingNesting.incrementAndGet();
+            }
+        } else {
+            if (DEBUG_PSS) {
+                Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
+            }
+        }
+    }
+
+    /**
+     * Schedule PSS collection of all processes.
+     */
+    @GuardedBy("mService")
+    void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
+        if (!always) {
+            if (now < (mLastFullPssTime
+                    + (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL
+                    : mService.mConstants.FULL_PSS_MIN_INTERVAL))) {
+                return;
+            }
+        }
+        if (DEBUG_PSS) {
+            Slog.d(TAG_PSS, "Requesting pss of all procs!  memLowered=" + memLowered);
+        }
+        mLastFullPssTime = now;
+        mFullPssPending = true;
+        for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
+            abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);
+        }
+        mPendingPssProcesses.ensureCapacity(mService.mProcessList.getLruSizeLocked());
+        mPendingPssProcesses.clear();
+        for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+            ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+            if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
+                continue;
+            }
+            if (memLowered || (always && now
+                    > app.lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
+                    || now > (app.lastStateTime + ProcessList.PSS_ALL_INTERVAL)) {
+                app.pssProcState = app.setProcState;
+                app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
+                        : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
+                updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
+                mPendingPssProcesses.add(app);
+            }
+        }
+        if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
+            mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
+        }
+    }
+
+    void setTestPssMode(boolean enabled) {
+        synchronized (mService) {
+            mTestPssMode = enabled;
+            if (enabled) {
+                // Whenever we enable the mode, we want to take a snapshot all of current
+                // process mem use.
+                requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    boolean getTestPssModeLocked() {
+        return mTestPssMode;
+    }
+
+    @GuardedBy("mService")
+    int getLastMemoryLevelLocked() {
+        return mLastMemoryLevel;
+    }
+
+    @GuardedBy("mService")
+    boolean isLastMemoryLevelNormal() {
+        return mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+    }
+
+    @GuardedBy("mService")
+    void updateLowRamTimestampLocked(long now) {
+        mLowRamTimeSinceLastIdle = 0;
+        if (mLowRamStartTime != 0) {
+            mLowRamStartTime = now;
+        }
+    }
+
+    @GuardedBy("mService")
+    void setAllowLowerMemLevelLocked(boolean allowLowerMemLevel) {
+        mAllowLowerMemLevel = allowLowerMemLevel;
+    }
+
+    @GuardedBy("mService")
+    boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
+        final int numOfLru = mService.mProcessList.getLruSizeLocked();
+        final long now = SystemClock.uptimeMillis();
+        int memFactor;
+        if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
+            memFactor = mLowMemDetector.getMemFactor();
+        } else {
+            // Now determine the memory trimming level of background processes.
+            // Unfortunately we need to start at the back of the list to do this
+            // properly.  We only do this if the number of background apps we
+            // are managing to keep around is less than half the maximum we desire;
+            // if we are keeping a good number around, we'll let them use whatever
+            // memory they want.
+            if (numCached <= mService.mConstants.CUR_TRIM_CACHED_PROCESSES
+                    && numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {
+                final int numCachedAndEmpty = numCached + numEmpty;
+                if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
+                    memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+                } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
+                    memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
+                } else {
+                    memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+                }
+            } else {
+                memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+            }
+        }
+        // We always allow the memory level to go up (better).  We only allow it to go
+        // down if we are in a state where that is allowed, *and* the total number of processes
+        // has gone down since last time.
+        if (DEBUG_OOM_ADJ) {
+            Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+                    + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
+                    + " numProcs=" + mService.mProcessList.getLruSizeLocked()
+                    + " last=" + mLastNumProcesses);
+        }
+        if (memFactor > mLastMemoryLevel) {
+            if (!mAllowLowerMemLevel
+                    || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
+                memFactor = mLastMemoryLevel;
+                if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
+            }
+        }
+        if (memFactor != mLastMemoryLevel) {
+            EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
+            FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
+        }
+        mLastMemoryLevel = memFactor;
+        mLastNumProcesses = mService.mProcessList.getLruSizeLocked();
+        boolean allChanged;
+        int trackerMemFactor;
+        synchronized (mService.mProcessStats.mLock) {
+            allChanged = mService.mProcessStats.setMemFactorLocked(memFactor,
+                    mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
+            trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
+        }
+        if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
+            if (mLowRamStartTime == 0) {
+                mLowRamStartTime = now;
+            }
+            int step = 0;
+            int fgTrimLevel;
+            switch (memFactor) {
+                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+                    break;
+                case ProcessStats.ADJ_MEM_FACTOR_LOW:
+                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
+                    break;
+                default:
+                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
+                    break;
+            }
+            int factor = numTrimming / 3;
+            int minFactor = 2;
+            if (mService.mAtmInternal.getHomeProcess() != null) minFactor++;
+            if (mService.mAtmInternal.getPreviousProcess() != null) minFactor++;
+            if (factor < minFactor) factor = minFactor;
+            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
+            for (int i = numOfLru - 1; i >= 0; i--) {
+                ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+                if (allChanged || app.procStateChanged) {
+                    mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
+                    app.procStateChanged = false;
+                }
+                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+                        && !app.killedByAm) {
+                    if (app.trimMemoryLevel < curLevel && app.thread != null) {
+                        try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+                                Slog.v(TAG_OOM_ADJ,
+                                        "Trimming memory of " + app.processName
+                                        + " to " + curLevel);
+                            }
+                            app.thread.scheduleTrimMemory(curLevel);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = curLevel;
+                    step++;
+                    if (step >= factor) {
+                        step = 0;
+                        switch (curLevel) {
+                            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
+                                break;
+                            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+                                break;
+                        }
+                    }
+                } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+                        && !app.killedByAm) {
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
+                            && app.thread != null) {
+                        try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+                                Slog.v(TAG_OOM_ADJ,
+                                        "Trimming memory of heavy-weight " + app.processName
+                                        + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
+                            }
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+                } else {
+                    if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                            || app.systemNoUi) && app.hasPendingUiClean()) {
+                        // If this application is now in the background and it
+                        // had done UI, then give it the special trim level to
+                        // have it free UI resources.
+                        final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+                        if (app.trimMemoryLevel < level && app.thread != null) {
+                            try {
+                                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+                                    Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
+                                            + app.processName + " to " + level);
+                                }
+                                app.thread.scheduleTrimMemory(level);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                        app.setPendingUiClean(false);
+                    }
+                    if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
+                        try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+                                Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
+                                        + " to " + fgTrimLevel);
+                            }
+                            app.thread.scheduleTrimMemory(fgTrimLevel);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = fgTrimLevel;
+                }
+            }
+        } else {
+            if (mLowRamStartTime != 0) {
+                mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
+                mLowRamStartTime = 0;
+            }
+            for (int i = numOfLru - 1; i >= 0; i--) {
+                ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+                if (allChanged || app.procStateChanged) {
+                    mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
+                    app.procStateChanged = false;
+                }
+                if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                        || app.systemNoUi) && app.hasPendingUiClean()) {
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+                            && app.thread != null) {
+                        try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+                                Slog.v(TAG_OOM_ADJ,
+                                        "Trimming memory of ui hidden " + app.processName
+                                        + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                            }
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.setPendingUiClean(false);
+                }
+                app.trimMemoryLevel = 0;
+            }
+        }
+        return allChanged;
+    }
+
+    @GuardedBy("mService")
+    long getLowRamTimeSinceIdleLocked(long now) {
+        return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
+    }
+
+    @GuardedBy("mService")
+    private void stopProfilerLocked(ProcessRecord proc, int profileType) {
+        if (proc == null || proc == mProfileData.getProfileProc()) {
+            proc = mProfileData.getProfileProc();
+            profileType = mProfileType;
+            clearProfilerLocked();
+        }
+        if (proc == null) {
+            return;
+        }
+        try {
+            proc.thread.profilerControl(false, null, profileType);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Process disappeared");
+        }
+    }
+
+    @GuardedBy("mService")
+    void clearProfilerLocked() {
+        if (mProfileData.getProfilerInfo() != null
+                && mProfileData.getProfilerInfo().profileFd != null) {
+            try {
+                mProfileData.getProfilerInfo().profileFd.close();
+            } catch (IOException e) {
+            }
+        }
+        mProfileData.setProfileApp(null);
+        mProfileData.setProfileProc(null);
+        mProfileData.setProfilerInfo(null);
+    }
+
+    @GuardedBy("mService")
+    void clearProfilerLocked(ProcessRecord app) {
+        if (mProfileData.getProfileProc() == null
+                || mProfileData.getProfilerInfo() == null
+                || mProfileData.getProfileProc() != app) {
+            return;
+        }
+        clearProfilerLocked();
+    }
+
+    @GuardedBy("mService")
+    boolean profileControlLocked(ProcessRecord proc, boolean start,
+            ProfilerInfo profilerInfo, int profileType) {
+        try {
+            if (start) {
+                stopProfilerLocked(null, 0);
+                mService.setProfileApp(proc.info, proc.processName, profilerInfo);
+                mProfileData.setProfileProc(proc);
+                mProfileType = profileType;
+                ParcelFileDescriptor fd = profilerInfo.profileFd;
+                try {
+                    fd = fd.dup();
+                } catch (IOException e) {
+                    fd = null;
+                }
+                profilerInfo.profileFd = fd;
+                proc.thread.profilerControl(start, profilerInfo, profileType);
+                fd = null;
+                try {
+                    mProfileData.getProfilerInfo().profileFd.close();
+                } catch (IOException e) {
+                }
+                mProfileData.getProfilerInfo().profileFd = null;
+
+                if (proc.pid == mService.MY_PID) {
+                    // When profiling the system server itself, avoid closing the file
+                    // descriptor, as profilerControl will not create a copy.
+                    // Note: it is also not correct to just set profileFd to null, as the
+                    //       whole ProfilerInfo instance is passed down!
+                    profilerInfo = null;
+                }
+            } else {
+                stopProfilerLocked(proc, profileType);
+                if (profilerInfo != null && profilerInfo.profileFd != null) {
+                    try {
+                        profilerInfo.profileFd.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+
+            return true;
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Process disappeared");
+        } finally {
+            if (profilerInfo != null && profilerInfo.profileFd != null) {
+                try {
+                    profilerInfo.profileFd.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void setProfileAppLocked(String processName, ProfilerInfo profilerInfo) {
+        mProfileData.setProfileApp(processName);
+
+        if (mProfileData.getProfilerInfo() != null) {
+            if (mProfileData.getProfilerInfo().profileFd != null) {
+                try {
+                    mProfileData.getProfilerInfo().profileFd.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
+        mProfileType = 0;
+    }
+
+    @GuardedBy("mService")
+    void setProfileProcLocked(ProcessRecord proc) {
+        mProfileData.setProfileProc(proc);
+    }
+
+    @GuardedBy("mService")
+    void setAgentAppLocked(@NonNull String packageName, @Nullable String agent) {
+        if (agent == null) {
+            if (mAppAgentMap != null) {
+                mAppAgentMap.remove(packageName);
+                if (mAppAgentMap.isEmpty()) {
+                    mAppAgentMap = null;
+                }
+            }
+        } else {
+            if (mAppAgentMap == null) {
+                mAppAgentMap = new HashMap<>();
+            }
+            if (mAppAgentMap.size() >= 100) {
+                // Limit the size of the map, to avoid OOMEs.
+                Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+                        + "/" + agent);
+                return;
+            }
+            mAppAgentMap.put(packageName, agent);
+        }
+    }
+
+    @GuardedBy("mService")
+    void updateCpuStatsLocked() {
+        final long now = SystemClock.uptimeMillis();
+        if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
+            return;
+        }
+        if (mProcessCpuMutexFree.compareAndSet(true, false)) {
+            synchronized (mProcessCpuThread) {
+                mProcessCpuThread.notify();
+            }
+        }
+    }
+
+    void updateCpuStatsNow() {
+        synchronized (mProcessCpuTracker) {
+            mProcessCpuMutexFree.set(false);
+            final long now = SystemClock.uptimeMillis();
+            boolean haveNewCpuStats = false;
+
+            if (MONITOR_CPU_USAGE
+                    && mLastCpuTime.get() < (now - MONITOR_CPU_MIN_TIME)) {
+                mLastCpuTime.set(now);
+                mProcessCpuTracker.update();
+                if (mProcessCpuTracker.hasGoodLastStats()) {
+                    haveNewCpuStats = true;
+                    //Slog.i(TAG, mProcessCpu.printCurrentState());
+                    //Slog.i(TAG, "Total CPU usage: "
+                    //        + mProcessCpu.getTotalCpuPercent() + "%");
+
+                    // Slog the cpu usage if the property is set.
+                    if ("true".equals(SystemProperties.get("events.cpu"))) {
+                        int user = mProcessCpuTracker.getLastUserTime();
+                        int system = mProcessCpuTracker.getLastSystemTime();
+                        int iowait = mProcessCpuTracker.getLastIoWaitTime();
+                        int irq = mProcessCpuTracker.getLastIrqTime();
+                        int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
+                        int idle = mProcessCpuTracker.getLastIdleTime();
+
+                        int total = user + system + iowait + irq + softIrq + idle;
+                        if (total == 0) total = 1;
+
+                        EventLogTags.writeCpu(
+                                ((user + system + iowait + irq + softIrq) * 100) / total,
+                                (user * 100) / total,
+                                (system * 100) / total,
+                                (iowait * 100) / total,
+                                (irq * 100) / total,
+                                (softIrq * 100) / total);
+                    }
+                }
+            }
+
+            final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics();
+            synchronized (bstats) {
+                if (haveNewCpuStats) {
+                    if (bstats.startAddingCpuLocked()) {
+                        int totalUTime = 0;
+                        int totalSTime = 0;
+                        final int statsCount = mProcessCpuTracker.countStats();
+                        final long elapsedRealtime = SystemClock.elapsedRealtime();
+                        final long uptime = SystemClock.uptimeMillis();
+                        synchronized (mService.mPidsSelfLocked) {
+                            for (int i = 0; i < statsCount; i++) {
+                                ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+                                if (!st.working) {
+                                    continue;
+                                }
+                                ProcessRecord pr = mService.mPidsSelfLocked.get(st.pid);
+                                totalUTime += st.rel_utime;
+                                totalSTime += st.rel_stime;
+                                if (pr != null) {
+                                    BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
+                                    if (ps == null || !ps.isActive()) {
+                                        pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
+                                                pr.info.uid, pr.processName,
+                                                elapsedRealtime, uptime);
+                                    }
+                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                    pr.curCpuTime += st.rel_utime + st.rel_stime;
+                                    if (pr.lastCpuTime == 0) {
+                                        pr.lastCpuTime = pr.curCpuTime;
+                                    }
+                                } else {
+                                    BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
+                                    if (ps == null || !ps.isActive()) {
+                                        st.batteryStats = ps = bstats.getProcessStatsLocked(
+                                                bstats.mapUid(st.uid), st.name,
+                                                elapsedRealtime, uptime);
+                                    }
+                                    ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
+                                }
+                            }
+                        }
+
+                        final int userTime = mProcessCpuTracker.getLastUserTime();
+                        final int systemTime = mProcessCpuTracker.getLastSystemTime();
+                        final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime();
+                        final int irqTime = mProcessCpuTracker.getLastIrqTime();
+                        final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
+                        final int idleTime = mProcessCpuTracker.getLastIdleTime();
+                        bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
+                                systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
+                    }
+                }
+
+                if (mLastWriteTime < (now - BATTERY_STATS_TIME)) {
+                    mLastWriteTime = now;
+                    mService.mBatteryStatsService.scheduleWriteToDisk();
+                }
+            }
+        }
+    }
+
+    long getCpuTimeForPid(int pid) {
+        synchronized (mProcessCpuTracker) {
+            return mProcessCpuTracker.getCpuTimeForPid(pid);
+        }
+    }
+
+    List<ProcessCpuTracker.Stats> getCpuStats(Predicate<ProcessCpuTracker.Stats> predicate) {
+        synchronized (mProcessCpuTracker) {
+            return mProcessCpuTracker.getStats(st -> predicate.test(st));
+        }
+    }
+
+    void forAllCpuStats(Consumer<ProcessCpuTracker.Stats> consumer) {
+        synchronized (mProcessCpuTracker) {
+            final int numOfStats = mProcessCpuTracker.countStats();
+            for (int i = 0; i < numOfStats; i++) {
+                consumer.accept(mProcessCpuTracker.getStats(i));
+            }
+        }
+    }
+
+    private class ProcessCpuThread extends Thread {
+        ProcessCpuThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void run() {
+            synchronized (mProcessCpuTracker) {
+                mProcessCpuInitLatch.countDown();
+                mProcessCpuTracker.init();
+            }
+            while (true) {
+                try {
+                    try {
+                        synchronized (this) {
+                            final long now = SystemClock.uptimeMillis();
+                            long nextCpuDelay = (mLastCpuTime.get() + MONITOR_CPU_MAX_TIME) - now;
+                            long nextWriteDelay = (mLastWriteTime + BATTERY_STATS_TIME) - now;
+                            //Slog.i(TAG, "Cpu delay=" + nextCpuDelay
+                            //        + ", write delay=" + nextWriteDelay);
+                            if (nextWriteDelay < nextCpuDelay) {
+                                nextCpuDelay = nextWriteDelay;
+                            }
+                            if (nextCpuDelay > 0) {
+                                mProcessCpuMutexFree.set(true);
+                                this.wait(nextCpuDelay);
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                    }
+                    updateCpuStatsNow();
+                } catch (Exception e) {
+                    Slog.e(TAG, "Unexpected exception collecting process stats", e);
+                }
+            }
+        }
+    }
+
+    class CpuBinder extends Binder {
+        private final PriorityDump.PriorityDumper mPriorityDumper =
+                new PriorityDump.PriorityDumper() {
+            @Override
+            public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
+                    boolean asProto) {
+                if (!DumpUtils.checkDumpAndUsageStatsPermission(mService.mContext, "cpuinfo", pw)) {
+                    return;
+                }
+                synchronized (mProcessCpuTracker) {
+                    if (asProto) {
+                        mProcessCpuTracker.dumpProto(fd);
+                        return;
+                    }
+                    pw.print(mProcessCpuTracker.printCurrentLoad());
+                    pw.print(mProcessCpuTracker.printCurrentState(
+                            SystemClock.uptimeMillis()));
+                }
+            }
+        };
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            PriorityDump.dump(mPriorityDumper, fd, pw, args);
+        }
+    }
+
+    void setCpuInfoService() {
+        if (MONITOR_CPU_USAGE) {
+            ServiceManager.addService("cpuinfo", new CpuBinder(),
+                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
+        }
+    }
+
+    AppProfiler(ActivityManagerService service, Looper bgLooper, LowMemDetector detector) {
+        mService = service;
+        mBgHandler = new BgHandler(bgLooper);
+        mLowMemDetector = detector;
+        mProcessCpuThread = new ProcessCpuThread("CpuTracker");
+    }
+
+    void retrieveSettings() {
+        final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ActivityThread.currentApplication().getMainExecutor(),
+                mPssDelayConfigListener);
+        mPssDeferralTime = pssDeferralMs;
+    }
+
+    void onActivityManagerInternalAdded() {
+        mProcessCpuThread.start();
+        // Wait for the synchronized block started in mProcessCpuThread,
+        // so that any other access to mProcessCpuTracker from main thread
+        // will be blocked during mProcessCpuTracker initialization.
+        try {
+            mProcessCpuInitLatch.await();
+        } catch (InterruptedException e) {
+            Slog.wtf(TAG, "Interrupted wait during start", e);
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException("Interrupted wait during start");
+        }
+    }
+
+    void onActivityLaunched() {
+        // This is safe to force to the head of the queue because it relies only
+        // on refcounting to track begin/end of deferrals, not on actual
+        // message ordering.  We don't care *what* activity is being
+        // launched; only that we're doing so.
+        if (mPssDeferralTime > 0) {
+            final Message msg = mBgHandler.obtainMessage(BgHandler.DEFER_PSS_MSG);
+            mBgHandler.sendMessageAtFrontOfQueue(msg);
+        }
+    }
+
+    @GuardedBy("mService")
+    ProfilerInfo setupProfilerInfoLocked(@NonNull IApplicationThread thread, ProcessRecord app,
+            ActiveInstrumentation instr) throws IOException, RemoteException {
+        ProfilerInfo profilerInfo = null;
+        String preBindAgent = null;
+        final String processName = app.processName;
+        if (mProfileData.getProfileApp() != null
+                && mProfileData.getProfileApp().equals(processName)) {
+            mProfileData.setProfileProc(app);
+            if (mProfileData.getProfilerInfo() != null) {
+                // Send a profiler info object to the app if either a file is given, or
+                // an agent should be loaded at bind-time.
+                boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
+                        || mProfileData.getProfilerInfo().attachAgentDuringBind;
+                profilerInfo = needsInfo
+                        ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
+                if (mProfileData.getProfilerInfo().agent != null) {
+                    preBindAgent = mProfileData.getProfilerInfo().agent;
+                }
+            }
+        } else if (instr != null && instr.mProfileFile != null) {
+            profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
+                    null, false);
+        }
+        if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+            // We need to do a debuggable check here. See setAgentApp for why the check is
+            // postponed to here.
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                String agent = mAppAgentMap.get(processName);
+                // Do not overwrite already requested agent.
+                if (profilerInfo == null) {
+                    profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+                            mAppAgentMap.get(processName), true);
+                } else if (profilerInfo.agent == null) {
+                    profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+                }
+            }
+        }
+
+        if (profilerInfo != null && profilerInfo.profileFd != null) {
+            profilerInfo.profileFd = profilerInfo.profileFd.dup();
+            if (TextUtils.equals(mProfileData.getProfileApp(), processName)
+                    && mProfileData.getProfilerInfo() != null) {
+                clearProfilerLocked();
+            }
+        }
+
+        // Check if this is a secondary process that should be incorporated into some
+        // currently active instrumentation.  (Note we do this AFTER all of the profiling
+        // stuff above because profiling can currently happen only in the primary
+        // instrumentation process.)
+        if (mService.mActiveInstrumentation.size() > 0 && instr == null) {
+            for (int i = mService.mActiveInstrumentation.size() - 1;
+                    i >= 0 && app.getActiveInstrumentation() == null; i--) {
+                ActiveInstrumentation aInstr = mService.mActiveInstrumentation.get(i);
+                if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
+                    if (aInstr.mTargetProcesses.length == 0) {
+                        // This is the wildcard mode, where every process brought up for
+                        // the target instrumentation should be included.
+                        if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
+                            app.setActiveInstrumentation(aInstr);
+                            aInstr.mRunningProcesses.add(app);
+                        }
+                    } else {
+                        for (String proc : aInstr.mTargetProcesses) {
+                            if (proc.equals(app.processName)) {
+                                app.setActiveInstrumentation(aInstr);
+                                aInstr.mRunningProcesses.add(app);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // If we were asked to attach an agent on startup, do so now, before we're binding
+        // application code.
+        if (preBindAgent != null) {
+            thread.attachAgent(preBindAgent);
+        }
+        if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+            thread.attachStartupAgents(app.info.dataDir);
+        }
+        return profilerInfo;
+    }
+
+    @GuardedBy("mService")
+    void onCleanupApplicationRecordLocked(ProcessRecord app) {
+        mPendingPssProcesses.remove(app);
+        abortNextPssTime(app.procStateMemTracker);
+    }
+
+    @GuardedBy("mService")
+    void onAppDiedLocked(ProcessRecord app) {
+        if (mProfileData.getProfileProc() == app) {
+            clearProfilerLocked();
+        }
+    }
+
+    @GuardedBy("mService")
+    boolean dumpMemWatchProcessesLocked(PrintWriter pw, boolean needSep) {
+        if (mMemWatchProcesses.getMap().size() > 0) {
+            pw.println("  Mem watch processes:");
+            final ArrayMap<String, SparseArray<Pair<Long, String>>> procs =
+                    mMemWatchProcesses.getMap();
+            for (int i = procs.size() - 1; i >= 0; i--) {
+                final String proc = procs.keyAt(i);
+                final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+                for (int j = uids.size() - 1; j >= 0; j--) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("    ").append(proc).append('/');
+                    UserHandle.formatUid(sb, uids.keyAt(j));
+                    Pair<Long, String> val = uids.valueAt(j);
+                    sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
+                    if (val.second != null) {
+                        sb.append(", report to ").append(val.second);
+                    }
+                    pw.println(sb.toString());
+                }
+            }
+            pw.print("  mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
+            pw.print("  mMemWatchDumpUri="); pw.println(mMemWatchDumpUri);
+            pw.print("  mMemWatchDumpPid="); pw.println(mMemWatchDumpPid);
+            pw.print("  mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
+            pw.print("  mMemWatchIsUserInitiated="); pw.println(mMemWatchIsUserInitiated);
+        }
+        return needSep;
+    }
+
+    @GuardedBy("mService")
+    boolean dumpProfileDataLocked(PrintWriter pw, String dumpPackage, boolean needSep) {
+        if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
+                || (mProfileData.getProfilerInfo() != null
+                && (mProfileData.getProfilerInfo().profileFile != null
+                        || mProfileData.getProfilerInfo().profileFd != null))) {
+            if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
+                if (needSep) {
+                    pw.println();
+                    needSep = false;
+                }
+                pw.println("  mProfileApp=" + mProfileData.getProfileApp()
+                        + " mProfileProc=" + mProfileData.getProfileProc());
+                if (mProfileData.getProfilerInfo() != null) {
+                    pw.println("  mProfileFile=" + mProfileData.getProfilerInfo().profileFile
+                            + " mProfileFd=" + mProfileData.getProfilerInfo().profileFd);
+                    pw.println("  mSamplingInterval="
+                            + mProfileData.getProfilerInfo().samplingInterval
+                            + " mAutoStopProfiler="
+                            + mProfileData.getProfilerInfo().autoStopProfiler
+                            + " mStreamingOutput="
+                            + mProfileData.getProfilerInfo().streamingOutput);
+                    pw.println("  mProfileType=" + mProfileType);
+                }
+            }
+        }
+        return needSep;
+    }
+
+    @GuardedBy("mService")
+    void dumpLastMemoryLevelLocked(PrintWriter pw) {
+        switch (mLastMemoryLevel) {
+            case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
+                pw.println("normal)");
+                break;
+            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+                pw.println("moderate)");
+                break;
+            case ProcessStats.ADJ_MEM_FACTOR_LOW:
+                pw.println("low)");
+                break;
+            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+                pw.println("critical)");
+                break;
+            default:
+                pw.print(mLastMemoryLevel);
+                pw.println(")");
+                break;
+        }
+    }
+
+    @GuardedBy("mService")
+    void dumpMemoryLevelsLocked(PrintWriter pw) {
+        pw.println("  mAllowLowerMemLevel=" + mAllowLowerMemLevel
+                + " mLastMemoryLevel=" + mLastMemoryLevel
+                + " mLastNumProcesses=" + mLastNumProcesses);
+    }
+
+    @GuardedBy("mService")
+    void writeMemWatchProcessToProtoLocked(ProtoOutputStream proto) {
+        if (mMemWatchProcesses.getMap().size() > 0) {
+            final long token = proto.start(
+                    ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
+            ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
+            for (int i = 0; i < procs.size(); i++) {
+                final String proc = procs.keyAt(i);
+                final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+                final long ptoken = proto.start(
+                        ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
+                proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME,
+                        proc);
+                for (int j = uids.size() - 1; j >= 0; j--) {
+                    final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto
+                            .MemWatchProcess.Process.MEM_STATS);
+                    Pair<Long, String> val = uids.valueAt(j);
+                    proto.write(ActivityManagerServiceDumpProcessesProto
+                            .MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
+                    proto.write(ActivityManagerServiceDumpProcessesProto
+                            .MemWatchProcess.Process.MemStats.SIZE,
+                            DebugUtils.sizeValueToString(val.first, new StringBuilder()));
+                    proto.write(ActivityManagerServiceDumpProcessesProto
+                            .MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
+                    proto.end(utoken);
+                }
+                proto.end(ptoken);
+            }
+
+            final long dtoken = proto.start(
+                    ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
+            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME,
+                    mMemWatchDumpProcName);
+            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.URI,
+                    mMemWatchDumpUri.toString());
+            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID,
+                    mMemWatchDumpPid);
+            proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID,
+                    mMemWatchDumpUid);
+            proto.write(
+                    ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.IS_USER_INITIATED,
+                    mMemWatchIsUserInitiated);
+            proto.end(dtoken);
+
+            proto.end(token);
+        }
+    }
+
+    @GuardedBy("mService")
+    void writeProfileDataToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
+        if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
+                || (mProfileData.getProfilerInfo() != null
+                && (mProfileData.getProfilerInfo().profileFile != null
+                        || mProfileData.getProfilerInfo().profileFd != null))) {
+            if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
+                final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
+                proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME,
+                        mProfileData.getProfileApp());
+                mProfileData.getProfileProc().dumpDebug(proto,
+                        ActivityManagerServiceDumpProcessesProto.Profile.PROC);
+                if (mProfileData.getProfilerInfo() != null) {
+                    mProfileData.getProfilerInfo().dumpDebug(proto,
+                            ActivityManagerServiceDumpProcessesProto.Profile.INFO);
+                    proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE,
+                            mProfileType);
+                }
+                proto.end(token);
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void writeMemoryLevelsToProtoLocked(ProtoOutputStream proto) {
+        proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL,
+                mAllowLowerMemLevel);
+        proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
+        proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
+    }
+
+    void printCurrentCpuState(StringBuilder report, long time) {
+        synchronized (mProcessCpuTracker) {
+            report.append(mProcessCpuTracker.printCurrentState(time));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 39f79ca..692b3f1 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -376,7 +376,8 @@
                     for (int uid : uidsToRemove) {
                         FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, uid,
                                 FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
-                        mStats.removeIsolatedUidLocked(uid);
+                        mStats.removeIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
+                                SystemClock.uptimeMillis());
                     }
                     mStats.clearPendingRemovedUids();
                 }
@@ -473,11 +474,15 @@
         final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
         final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
         final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        final long elapsedRealtimeUs = elapsedRealtime * 1000;
+        final long uptimeUs = uptime * 1000;
 
         synchronized (mStats) {
             mStats.addHistoryEventLocked(
-                    SystemClock.elapsedRealtime(),
-                    SystemClock.uptimeMillis(),
+                    elapsedRealtime,
+                    uptime,
                     BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
                     reason, 0);
 
@@ -490,17 +495,17 @@
             }
 
             if ((updateFlags & UPDATE_ALL) != 0) {
-                mStats.updateKernelWakelocksLocked();
-                mStats.updateKernelMemoryBandwidthLocked();
+                mStats.updateKernelWakelocksLocked(elapsedRealtimeUs);
+                mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs);
             }
 
             if ((updateFlags & UPDATE_RPM) != 0) {
-                mStats.updateRpmStatsLocked();
+                mStats.updateRpmStatsLocked(elapsedRealtimeUs);
             }
 
             if (bluetoothInfo != null) {
                 if (bluetoothInfo.isValid()) {
-                    mStats.updateBluetoothStateLocked(bluetoothInfo);
+                    mStats.updateBluetoothStateLocked(bluetoothInfo, elapsedRealtime, uptime);
                 } else {
                     Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo);
                 }
@@ -512,7 +517,7 @@
 
         if (wifiInfo != null) {
             if (wifiInfo.isValid()) {
-                mStats.updateWifiState(extractDeltaLocked(wifiInfo));
+                mStats.updateWifiState(extractDeltaLocked(wifiInfo), elapsedRealtime, uptime);
             } else {
                 Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
             }
@@ -520,7 +525,7 @@
 
         if (modemInfo != null) {
             if (modemInfo.isValid()) {
-                mStats.updateMobileRadioState(modemInfo);
+                mStats.updateMobileRadioState(modemInfo, elapsedRealtime, uptime);
             } else {
                 Slog.w(TAG, "modem info is invalid: " + modemInfo);
             }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d72998b..f427532 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -25,6 +25,7 @@
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
@@ -62,7 +63,9 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ParseUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
+import com.android.server.Watchdog;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -76,6 +79,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 
@@ -86,7 +90,8 @@
 public final class BatteryStatsService extends IBatteryStats.Stub
         implements PowerManagerInternal.LowPowerModeListener,
         BatteryStatsImpl.PlatformIdleStateCallback,
-        BatteryStatsImpl.RailEnergyDataCallback {
+        BatteryStatsImpl.RailEnergyDataCallback,
+        Watchdog.Monitor {
     static final String TAG = "BatteryStatsService";
     static final boolean DBG = false;
 
@@ -110,6 +115,10 @@
     private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
     private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
 
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
+    private final Object mLock = new Object();
+
     /**
      * Replaces the information in the given rpmStats with up-to-date information.
      */
@@ -190,6 +199,9 @@
                 return (umi != null) ? umi.getUserIds() : null;
             }
         };
+        mHandlerThread = new HandlerThread("batterystats-handler");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
         mStats = new BatteryStatsImpl(systemDir, handler, this,
                 this, mUserManagerUserInfoProvider);
         mWorker = new BatteryExternalStatsWorker(context, mStats);
@@ -206,6 +218,7 @@
 
     public void systemServicesReady() {
         mStats.systemServicesReady(mContext);
+        Watchdog.getInstance().addMonitor(this);
     }
 
     private final class LocalService extends BatteryStatsInternal {
@@ -228,8 +241,20 @@
         @Override
         public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
                 Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
-            mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats,
-                    binderThreadNativeTids);
+            synchronized (BatteryStatsService.this.mLock) {
+                mHandler.sendMessage(PooledLambda.obtainMessage(
+                        mStats::noteBinderCallStats, workSourceUid, incrementatCallCount,
+                        callStats, binderThreadNativeTids,
+                        SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()));
+            }
+        }
+    }
+
+    @Override
+    public void monitor() {
+        synchronized (mLock) {
+        }
+        synchronized (mStats) {
         }
     }
 
@@ -250,6 +275,19 @@
         awaitUninterruptibly(mWorker.scheduleSync(reason, flags));
     }
 
+    private void awaitCompletion() {
+        synchronized (mLock) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            mHandler.post(() -> {
+                latch.countDown();
+            });
+            try {
+                latch.await();
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
     /**
      * At the time when the constructor runs, the power manager has not yet been
      * initialized.  So we initialize the low power observer later.
@@ -259,8 +297,8 @@
         powerMgr.registerLowPowerModeObserver(this);
         synchronized (mStats) {
             mStats.notePowerSaveModeLocked(
-                    powerMgr.getLowPowerState(ServiceType.BATTERY_STATS)
-                            .batterySaverEnabled);
+                    powerMgr.getLowPowerState(ServiceType.BATTERY_STATS).batterySaverEnabled,
+                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
         }
         (new WakeupReasonThread()).start();
     }
@@ -268,6 +306,9 @@
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
 
+        // Drain the handler queue to make sure we've handled all pending works.
+        awaitCompletion();
+
         syncStats("shutdown", BatteryExternalStatsWorker.UPDATE_ALL);
 
         synchronized (mStats) {
@@ -293,9 +334,16 @@
     }
 
     @Override
-    public void onLowPowerModeChanged(PowerSaveState result) {
-        synchronized (mStats) {
-            mStats.notePowerSaveModeLocked(result.batterySaverEnabled);
+    public void onLowPowerModeChanged(final PowerSaveState result) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePowerSaveModeLocked(result.batterySaverEnabled,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
@@ -313,7 +361,12 @@
      * object to update with the latest info, then write to disk.
      */
     public void scheduleWriteToDisk() {
-        mWorker.scheduleWrite();
+        synchronized (mLock) {
+            // We still schedule it on the handler so we'll have all existing pending works done.
+            mHandler.post(() -> {
+                mWorker.scheduleWrite();
+            });
+        }
     }
 
     // These are for direct use by the activity manager...
@@ -321,70 +374,124 @@
     /**
      * Remove a UID from the BatteryStats and BatteryStats' external dependencies.
      */
-    void removeUid(int uid) {
-        synchronized (mStats) {
-            mStats.removeUidStatsLocked(uid);
+    void removeUid(final int uid) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.removeUidStatsLocked(uid, elapsedRealtime);
+                }
+            });
         }
     }
 
-    void onCleanupUser(int userId) {
-        synchronized (mStats) {
-            mStats.onCleanupUserLocked(userId);
+    void onCleanupUser(final int userId) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.onCleanupUserLocked(userId, elapsedRealtime);
+                }
+            });
         }
     }
 
-    void onUserRemoved(int userId) {
-        synchronized (mStats) {
-            mStats.onUserRemovedLocked(userId);
+    void onUserRemoved(final int userId) {
+        synchronized (mLock) {
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.onUserRemovedLocked(userId);
+                }
+            });
         }
     }
 
-    void addIsolatedUid(int isolatedUid, int appUid) {
-        synchronized (mStats) {
-            mStats.addIsolatedUidLocked(isolatedUid, appUid);
+    void addIsolatedUid(final int isolatedUid, final int appUid) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.addIsolatedUidLocked(isolatedUid, appUid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    void removeIsolatedUid(int isolatedUid, int appUid) {
-        synchronized (mStats) {
-            mStats.scheduleRemoveIsolatedUidLocked(isolatedUid, appUid);
+    void removeIsolatedUid(final int isolatedUid, final int appUid) {
+        synchronized (mLock) {
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.scheduleRemoveIsolatedUidLocked(isolatedUid, appUid);
+                }
+            });
         }
     }
 
-    void noteProcessStart(String name, int uid) {
-        synchronized (mStats) {
-            mStats.noteProcessStartLocked(name, uid);
-            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__STARTED);
+    void noteProcessStart(final String name, final int uid) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteProcessStartLocked(name, uid, elapsedRealtime, uptime);
+                }
+            });
         }
+        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__STARTED);
     }
 
     void noteProcessCrash(String name, int uid) {
-        synchronized (mStats) {
-            mStats.noteProcessCrashLocked(name, uid);
-            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__CRASHED);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteProcessCrashLocked(name, uid, elapsedRealtime, uptime);
+                }
+            });
         }
+        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__CRASHED);
     }
 
     void noteProcessAnr(String name, int uid) {
-        synchronized (mStats) {
-            mStats.noteProcessAnrLocked(name, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteProcessAnrLocked(name, uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     void noteProcessFinish(String name, int uid) {
-        synchronized (mStats) {
-            mStats.noteProcessFinishLocked(name, uid);
-            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__FINISHED);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteProcessFinishLocked(name, uid, elapsedRealtime, uptime);
+                }
+            });
         }
+        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+                FrameworkStatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__FINISHED);
     }
 
     /** @param state Process state from ActivityManager.java. */
     void noteUidProcessState(int uid, int state) {
-        synchronized (mStats) {
-            mStats.noteUidProcessStateLocked(uid, state);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteUidProcessStateLocked(uid, state, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
@@ -396,6 +503,9 @@
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
+        // Drain the handler queue to make sure we've handled all pending works, so we'll get
+        // an accurate stats.
+        awaitCompletion();
         syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
@@ -411,6 +521,9 @@
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
+        // Drain the handler queue to make sure we've handled all pending works, so we'll get
+        // an accurate stats.
+        awaitCompletion();
         syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
@@ -446,579 +559,1015 @@
         }
     }
 
-    public void noteEvent(int code, String name, int uid) {
+    public void noteEvent(final int code, final String name, final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteEventLocked(code, name, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteEventLocked(code, name, uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteSyncStart(String name, int uid) {
+    public void noteSyncStart(final String name, final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteSyncStartLocked(name, uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SYNC_STATE_CHANGED, uid, null,
-                    name, FrameworkStatsLog.SYNC_STATE_CHANGED__STATE__ON);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteSyncStartLocked(name, uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SYNC_STATE_CHANGED, uid, null,
+                        name, FrameworkStatsLog.SYNC_STATE_CHANGED__STATE__ON);
+            });
         }
     }
 
-    public void noteSyncFinish(String name, int uid) {
+    public void noteSyncFinish(final String name, final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteSyncFinishLocked(name, uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SYNC_STATE_CHANGED, uid, null,
-                    name, FrameworkStatsLog.SYNC_STATE_CHANGED__STATE__OFF);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteSyncFinishLocked(name, uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SYNC_STATE_CHANGED, uid, null,
+                        name, FrameworkStatsLog.SYNC_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
     /** A scheduled job was started. */
-    public void noteJobStart(String name, int uid) {
+    public void noteJobStart(final String name, final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteJobStartLocked(name, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteJobStartLocked(name, uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     /** A scheduled job was finished. */
-    public void noteJobFinish(String name, int uid, int stopReason) {
+    public void noteJobFinish(final String name, final int uid, final int stopReason) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteJobFinishLocked(name, uid, stopReason);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteJobFinishLocked(name, uid, stopReason, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    void noteJobsDeferred(int uid, int numDeferred, long sinceLast) {
+    void noteJobsDeferred(final int uid, final int numDeferred, final long sinceLast) {
         // No need to enforce calling permission, as it is called from an internal interface
-        synchronized (mStats) {
-            mStats.noteJobsDeferredLocked(uid, numDeferred, sinceLast);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteJobsDeferredLocked(uid, numDeferred, sinceLast,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWakupAlarm(String name, int uid, WorkSource workSource, String tag) {
+    public void noteWakupAlarm(final String name, final int uid, final WorkSource workSource,
+            final String tag) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWakupAlarmLocked(name, uid, workSource, tag);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWakupAlarmLocked(name, uid, workSource, tag,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteAlarmStart(String name, WorkSource workSource, int uid) {
+    public void noteAlarmStart(final String name, final WorkSource workSource, final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteAlarmStartLocked(name, workSource, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteAlarmStartLocked(name, workSource, uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteAlarmFinish(String name, WorkSource workSource, int uid) {
+    public void noteAlarmFinish(final String name, final WorkSource workSource, final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteAlarmFinishLocked(name, workSource, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteAlarmFinishLocked(name, workSource, uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
-            boolean unimportantForLogging) {
+    public void noteStartWakelock(final int uid, final int pid, final String name,
+            final String historyName, final int type, final boolean unimportantForLogging) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteStartWakeLocked(uid, pid, null, name, historyName, type,
-                    unimportantForLogging, SystemClock.elapsedRealtime(),
-                    SystemClock.uptimeMillis());
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteStartWakeLocked(uid, pid, null, name, historyName, type,
+                            unimportantForLogging, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteStopWakelock(int uid, int pid, String name, String historyName, int type) {
+    public void noteStopWakelock(final int uid, final int pid, final String name,
+            final String historyName, final int type) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteStopWakeLocked(uid, pid, null, name, historyName, type,
-                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteStopWakeLocked(uid, pid, null, name, historyName, type,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteStartWakelockFromSource(WorkSource ws, int pid, String name,
-            String historyName, int type, boolean unimportantForLogging) {
+    public void noteStartWakelockFromSource(final WorkSource ws, final int pid, final String name,
+            final String historyName, final int type, final boolean unimportantForLogging) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName,
-                    type, unimportantForLogging);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName,
+                            type, unimportantForLogging, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteChangeWakelockFromSource(WorkSource ws, int pid, String name,
-            String historyName, int type, WorkSource newWs, int newPid, String newName,
-            String newHistoryName, int newType, boolean newUnimportantForLogging) {
+    public void noteChangeWakelockFromSource(final WorkSource ws, final int pid, final String name,
+            final String historyName, final int type, final WorkSource newWs, final int newPid,
+            final String newName, final String newHistoryName, final int newType,
+            final boolean newUnimportantForLogging) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type,
-                    newWs, newPid, newName, newHistoryName, newType, newUnimportantForLogging);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type,
+                            newWs, newPid, newName, newHistoryName, newType,
+                            newUnimportantForLogging, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, String historyName,
-            int type) {
+    public void noteStopWakelockFromSource(final WorkSource ws, final int pid, final String name,
+            final String historyName, final int type) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
+    public void noteLongPartialWakelockStart(final String name, final String historyName,
+            final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteLongPartialWakelockStart(name, historyName, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteLongPartialWakelockStart(name, historyName, uid,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteLongPartialWakelockStartFromSource(String name, String historyName,
-            WorkSource workSource) {
+    public void noteLongPartialWakelockStartFromSource(final String name, final String historyName,
+            final WorkSource workSource) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteLongPartialWakelockStartFromSource(name, historyName, workSource);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteLongPartialWakelockStartFromSource(name, historyName, workSource,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
+    public void noteLongPartialWakelockFinish(final String name, final String historyName,
+            final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteLongPartialWakelockFinish(name, historyName, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteLongPartialWakelockFinish(name, historyName, uid,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
-            WorkSource workSource) {
+    public void noteLongPartialWakelockFinishFromSource(final String name, final String historyName,
+            final WorkSource workSource) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteLongPartialWakelockFinishFromSource(name, historyName, workSource);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteLongPartialWakelockFinishFromSource(name, historyName, workSource,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteStartSensor(int uid, int sensor) {
+    public void noteStartSensor(final int uid, final int sensor) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteStartSensorLocked(uid, sensor);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SENSOR_STATE_CHANGED, uid, null,
-                    sensor, FrameworkStatsLog.SENSOR_STATE_CHANGED__STATE__ON);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteStartSensorLocked(uid, sensor, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SENSOR_STATE_CHANGED, uid,
+                        null, sensor, FrameworkStatsLog.SENSOR_STATE_CHANGED__STATE__ON);
+            });
         }
     }
 
-    public void noteStopSensor(int uid, int sensor) {
+    public void noteStopSensor(final int uid, final int sensor) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteStopSensorLocked(uid, sensor);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SENSOR_STATE_CHANGED, uid, null,
-                    sensor, FrameworkStatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteStopSensorLocked(uid, sensor, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SENSOR_STATE_CHANGED, uid,
+                        null, sensor, FrameworkStatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
-    public void noteVibratorOn(int uid, long durationMillis) {
+    public void noteVibratorOn(final int uid, final long durationMillis) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteVibratorOnLocked(uid, durationMillis);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteVibratorOnLocked(uid, durationMillis, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteVibratorOff(int uid) {
+    public void noteVibratorOff(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteVibratorOffLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteVibratorOffLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteGpsChanged(WorkSource oldWs, WorkSource newWs) {
+    public void noteGpsChanged(final WorkSource oldWs, final WorkSource newWs) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteGpsChangedLocked(oldWs, newWs);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteGpsChangedLocked(oldWs, newWs, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteGpsSignalQuality(int signalLevel) {
-        synchronized (mStats) {
-            mStats.noteGpsSignalQualityLocked(signalLevel);
+    public void noteGpsSignalQuality(final int signalLevel) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteGpsSignalQualityLocked(signalLevel, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteScreenState(int state) {
+    public void noteScreenState(final int state) {
         enforceCallingPermission();
-        if (DBG) Slog.d(TAG, "begin noteScreenState");
-        synchronized (mStats) {
-            FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED, state);
-
-            mStats.noteScreenStateLocked(state);
-        }
-        if (DBG) Slog.d(TAG, "end noteScreenState");
-    }
-
-    public void noteScreenBrightness(int brightness) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED, brightness);
-            mStats.noteScreenBrightnessLocked(brightness);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            final long currentTime = System.currentTimeMillis();
+            mHandler.post(() -> {
+                if (DBG) Slog.d(TAG, "begin noteScreenState");
+                synchronized (mStats) {
+                    mStats.noteScreenStateLocked(state, elapsedRealtime, uptime, currentTime);
+                }
+                FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_STATE_CHANGED, state);
+                if (DBG) Slog.d(TAG, "end noteScreenState");
+            });
         }
     }
 
-    public void noteUserActivity(int uid, int event) {
+    public void noteScreenBrightness(final int brightness) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteUserActivityLocked(uid, event);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteScreenBrightnessLocked(brightness, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_BRIGHTNESS_CHANGED, brightness);
+            });
         }
     }
 
-    public void noteWakeUp(String reason, int reasonUid) {
+    public void noteUserActivity(final int uid, final int event) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWakeUpLocked(reason, reasonUid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteUserActivityLocked(uid, event, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteInteractive(boolean interactive) {
+    public void noteWakeUp(final String reason, final int reasonUid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteInteractiveLocked(interactive);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWakeUpLocked(reason, reasonUid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteConnectivityChanged(int type, String extra) {
+    public void noteInteractive(final boolean interactive) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteConnectivityChangedLocked(type, extra);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteInteractiveLocked(interactive, elapsedRealtime);
+                }
+            });
         }
     }
 
-    public void noteMobileRadioPowerState(int powerState, long timestampNs, int uid) {
+    public void noteConnectivityChanged(final int type, final String extra) {
         enforceCallingPermission();
-        final boolean update;
-        synchronized (mStats) {
-            update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteConnectivityChangedLocked(type, extra, elapsedRealtime, uptime);
+                }
+            });
         }
+    }
 
-        if (update) {
-            mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO);
+    public void noteMobileRadioPowerState(final int powerState, final long timestampNs,
+            final int uid) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                final boolean update;
+                synchronized (mStats) {
+                    update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
+                            elapsedRealtime, uptime);
+                }
+
+                if (update) {
+                    mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO);
+                }
+            });
         }
     }
 
     public void notePhoneOn() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.notePhoneOnLocked();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePhoneOnLocked(elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     public void notePhoneOff() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.notePhoneOffLocked();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePhoneOffLocked(elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void notePhoneSignalStrength(SignalStrength signalStrength) {
+    public void notePhoneSignalStrength(final SignalStrength signalStrength) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.notePhoneSignalStrengthLocked(signalStrength);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePhoneSignalStrengthLocked(signalStrength, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType) {
+    public void notePhoneDataConnectionState(final int dataType, final boolean hasData,
+            final int serviceType) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.notePhoneDataConnectionStateLocked(dataType, hasData, serviceType);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void notePhoneState(int state) {
+    public void notePhoneState(final int state) {
         enforceCallingPermission();
-        int simState = mContext.getSystemService(TelephonyManager.class).getSimState();
-        synchronized (mStats) {
-            mStats.notePhoneStateLocked(state, simState);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                int simState = mContext.getSystemService(TelephonyManager.class).getSimState();
+                synchronized (mStats) {
+                    mStats.notePhoneStateLocked(state, simState, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     public void noteWifiOn() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiOnLocked();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiOnLocked(elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write(FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED,
+                        FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED__STATE__ON);
+            });
         }
-        FrameworkStatsLog.write(FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED,
-                FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED__STATE__ON);
     }
 
     public void noteWifiOff() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiOffLocked();
-        }
-        FrameworkStatsLog.write(FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED,
-                FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED__STATE__OFF);
-    }
-
-    public void noteStartAudio(int uid) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteAudioOnLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.AUDIO_STATE_CHANGED, uid, null,
-                    FrameworkStatsLog.AUDIO_STATE_CHANGED__STATE__ON);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiOffLocked(elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write(FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED,
+                        FrameworkStatsLog.WIFI_ENABLED_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
-    public void noteStopAudio(int uid) {
+    public void noteStartAudio(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteAudioOffLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.AUDIO_STATE_CHANGED, uid, null,
-                    FrameworkStatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteAudioOnLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.AUDIO_STATE_CHANGED, uid,
+                        null, FrameworkStatsLog.AUDIO_STATE_CHANGED__STATE__ON);
+            });
         }
     }
 
-    public void noteStartVideo(int uid) {
+    public void noteStopAudio(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteVideoOnLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED, uid,
-                    null, FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__ON);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteAudioOffLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.AUDIO_STATE_CHANGED, uid,
+                        null, FrameworkStatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
-    public void noteStopVideo(int uid) {
+    public void noteStartVideo(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteVideoOffLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED, uid,
-                    null, FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__OFF);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteVideoOnLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED,
+                        uid, null, FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__ON);
+            });
+        }
+    }
+
+    public void noteStopVideo(final int uid) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteVideoOffLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED,
+                        uid, null, FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
     public void noteResetAudio() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteResetAudioLocked();
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.AUDIO_STATE_CHANGED, -1, null,
-                    FrameworkStatsLog.AUDIO_STATE_CHANGED__STATE__RESET);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteResetAudioLocked(elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.AUDIO_STATE_CHANGED, -1, null,
+                        FrameworkStatsLog.AUDIO_STATE_CHANGED__STATE__RESET);
+            });
         }
     }
 
     public void noteResetVideo() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteResetVideoLocked();
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED, -1,
-                    null, FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__RESET);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteResetVideoLocked(elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED, -1,
+                        null, FrameworkStatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__RESET);
+            });
         }
     }
 
-    public void noteFlashlightOn(int uid) {
+    public void noteFlashlightOn(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteFlashlightOnLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED, uid,
-                    null, FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteFlashlightOnLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED, uid,
+                        null, FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
+            });
         }
     }
 
-    public void noteFlashlightOff(int uid) {
+    public void noteFlashlightOff(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteFlashlightOffLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED, uid,
-                    null, FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteFlashlightOffLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED, uid,
+                        null, FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
-    public void noteStartCamera(int uid) {
+    public void noteStartCamera(final int uid) {
         enforceCallingPermission();
         if (DBG) Slog.d(TAG, "begin noteStartCamera");
-        synchronized (mStats) {
-            mStats.noteCameraOnLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.CAMERA_STATE_CHANGED, uid, null,
-                    FrameworkStatsLog.CAMERA_STATE_CHANGED__STATE__ON);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteCameraOnLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.CAMERA_STATE_CHANGED, uid,
+                        null, FrameworkStatsLog.CAMERA_STATE_CHANGED__STATE__ON);
+            });
         }
         if (DBG) Slog.d(TAG, "end noteStartCamera");
     }
 
-    public void noteStopCamera(int uid) {
+    public void noteStopCamera(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteCameraOffLocked(uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.CAMERA_STATE_CHANGED, uid, null,
-                    FrameworkStatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteCameraOffLocked(uid, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.CAMERA_STATE_CHANGED, uid,
+                        null, FrameworkStatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
     public void noteResetCamera() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteResetCameraLocked();
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.CAMERA_STATE_CHANGED, -1, null,
-                    FrameworkStatsLog.CAMERA_STATE_CHANGED__STATE__RESET);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteResetCameraLocked(elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.CAMERA_STATE_CHANGED, -1,
+                        null, FrameworkStatsLog.CAMERA_STATE_CHANGED__STATE__RESET);
+            });
         }
     }
 
     public void noteResetFlashlight() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteResetFlashlightLocked();
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED, -1,
-                    null, FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED__STATE__RESET);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteResetFlashlightLocked(elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED, -1,
+                        null, FrameworkStatsLog.FLASHLIGHT_STATE_CHANGED__STATE__RESET);
+            });
         }
     }
 
     @Override
-    public void noteWifiRadioPowerState(int powerState, long tsNanos, int uid) {
+    public void noteWifiRadioPowerState(final int powerState, final long tsNanos, final int uid) {
         enforceCallingPermission();
-
-        // There was a change in WiFi power state.
-        // Collect data now for the past activity.
-        synchronized (mStats) {
-            if (mStats.isOnBattery()) {
-                final String type = (powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH ||
-                        powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM) ? "active"
-                        : "inactive";
-                mWorker.scheduleSync("wifi-data: " + type, BatteryExternalStatsWorker.UPDATE_WIFI);
-            }
-            mStats.noteWifiRadioPowerState(powerState, tsNanos, uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                // There was a change in WiFi power state.
+                // Collect data now for the past activity.
+                synchronized (mStats) {
+                    if (mStats.isOnBattery()) {
+                        final String type =
+                                (powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+                                || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM)
+                                ? "active" : "inactive";
+                        mWorker.scheduleSync("wifi-data: " + type,
+                                BatteryExternalStatsWorker.UPDATE_WIFI);
+                    }
+                    mStats.noteWifiRadioPowerState(powerState, tsNanos, uid,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiRunning(WorkSource ws) {
+    public void noteWifiRunning(final WorkSource ws) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiRunningLocked(ws);
-        }
-        // TODO: Log WIFI_RUNNING_STATE_CHANGED in a better spot to include Hotspot too.
-        FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
-                ws, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
-    }
-
-    public void noteWifiRunningChanged(WorkSource oldWs, WorkSource newWs) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiRunningChangedLocked(oldWs, newWs);
-        }
-        FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
-                newWs, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
-        FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
-                oldWs, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
-    }
-
-    public void noteWifiStopped(WorkSource ws) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiStoppedLocked(ws);
-        }
-        FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
-                ws, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
-    }
-
-    public void noteWifiState(int wifiState, String accessPoint) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiStateLocked(wifiState, accessPoint);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiRunningLocked(ws, elapsedRealtime, uptime);
+                }
+                // TODO: Log WIFI_RUNNING_STATE_CHANGED in a better spot to include Hotspot too.
+                FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
+                        ws, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
+            });
         }
     }
 
-    public void noteWifiSupplicantStateChanged(int supplState, boolean failedAuth) {
+    public void noteWifiRunningChanged(final WorkSource oldWs, final WorkSource newWs) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiSupplicantStateChangedLocked(supplState, failedAuth);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiRunningChangedLocked(oldWs, newWs, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
+                        newWs, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON);
+                FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
+                        oldWs, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
-    public void noteWifiRssiChanged(int newRssi) {
+    public void noteWifiStopped(final WorkSource ws) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiRssiChangedLocked(newRssi);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiStoppedLocked(ws, elapsedRealtime, uptime);
+                }
+                FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED,
+                        ws, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF);
+            });
         }
     }
 
-    public void noteFullWifiLockAcquired(int uid) {
+    public void noteWifiState(final int wifiState, final String accessPoint) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteFullWifiLockAcquiredLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiStateLocked(wifiState, accessPoint, elapsedRealtime);
+                }
+            });
         }
     }
 
-    public void noteFullWifiLockReleased(int uid) {
+    public void noteWifiSupplicantStateChanged(final int supplState, final boolean failedAuth) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteFullWifiLockReleasedLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiSupplicantStateChangedLocked(supplState, failedAuth,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiScanStarted(int uid) {
+    public void noteWifiRssiChanged(final int newRssi) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiScanStartedLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiRssiChangedLocked(newRssi, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiScanStopped(int uid) {
+    public void noteFullWifiLockAcquired(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiScanStoppedLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteFullWifiLockAcquiredLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiMulticastEnabled(int uid) {
+    public void noteFullWifiLockReleased(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiMulticastEnabledLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteFullWifiLockReleasedLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiMulticastDisabled(int uid) {
+    public void noteWifiScanStarted(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiMulticastDisabledLocked(uid);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiScanStartedLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
+    public void noteWifiScanStopped(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiScanStoppedLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
+    public void noteWifiMulticastEnabled(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiMulticastEnabledLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiScanStartedFromSource(WorkSource ws) {
+    public void noteWifiMulticastDisabled(final int uid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiScanStartedFromSourceLocked(ws);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiMulticastDisabledLocked(uid, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiScanStoppedFromSource(WorkSource ws) {
+    public void noteFullWifiLockAcquiredFromSource(final WorkSource ws) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiScanStoppedFromSourceLocked(ws);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteFullWifiLockAcquiredFromSourceLocked(ws, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiBatchedScanStartedFromSource(WorkSource ws, int csph) {
+    public void noteFullWifiLockReleasedFromSource(final WorkSource ws) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteFullWifiLockReleasedFromSourceLocked(ws, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
-    public void noteWifiBatchedScanStoppedFromSource(WorkSource ws) {
+    public void noteWifiScanStartedFromSource(final WorkSource ws) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiScanStartedFromSourceLocked(ws, elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    public void noteWifiScanStoppedFromSource(final WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiScanStoppedFromSourceLocked(ws, elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    public void noteWifiBatchedScanStartedFromSource(final WorkSource ws, final int csph) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph,
+                            elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    public void noteWifiBatchedScanStoppedFromSource(final WorkSource ws) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteNetworkInterfaceType(String iface, int networkType) {
+    public void noteNetworkInterfaceType(final String iface, final int networkType) {
         enforceCallingPermission();
-        mStats.noteNetworkInterfaceType(iface, networkType);
+        synchronized (mLock) {
+            mHandler.post(() -> {
+                mStats.noteNetworkInterfaceType(iface, networkType);
+            });
+        }
     }
 
     @Override
@@ -1027,66 +1576,119 @@
         // During device boot, qtaguid isn't enabled until after the inital
         // loading of battery stats. Now that they're enabled, take our initial
         // snapshot for future delta calculation.
-        mWorker.scheduleSync("network-stats-enabled",
-                BatteryExternalStatsWorker.UPDATE_RADIO | BatteryExternalStatsWorker.UPDATE_WIFI);
-    }
-
-    @Override
-    public void noteDeviceIdleMode(int mode, String activeReason, int activeUid) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid);
-        }
-    }
-
-    public void notePackageInstalled(String pkgName, long versionCode) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.notePackageInstalledLocked(pkgName, versionCode);
-        }
-    }
-
-    public void notePackageUninstalled(String pkgName) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.notePackageUninstalledLocked(pkgName);
+        synchronized (mLock) {
+            // Still schedule it on the handler to make sure we have existing pending works done
+            mHandler.post(() -> {
+                mWorker.scheduleSync("network-stats-enabled",
+                        BatteryExternalStatsWorker.UPDATE_RADIO
+                        | BatteryExternalStatsWorker.UPDATE_WIFI);
+            });
         }
     }
 
     @Override
-    public void noteBleScanStarted(WorkSource ws, boolean isUnoptimized) {
+    public void noteDeviceIdleMode(final int mode, final String activeReason, final int activeUid) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid,
+                            elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    public void notePackageInstalled(final String pkgName, final long versionCode) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePackageInstalledLocked(pkgName, versionCode,
+                            elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    public void notePackageUninstalled(final String pkgName) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.notePackageUninstalledLocked(pkgName, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteBleScanStopped(WorkSource ws, boolean isUnoptimized) {
+    public void noteBleScanStarted(final WorkSource ws, final boolean isUnoptimized) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized,
+                            elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    @Override
+    public void noteBleScanStopped(final WorkSource ws, final boolean isUnoptimized) {
+        enforceCallingPermission();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized,
+                            uptime, elapsedRealtime);
+                }
+            });
         }
     }
 
     @Override
     public void noteResetBleScan() {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteResetBluetoothScanLocked();
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteResetBluetoothScanLocked(elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteBleScanResults(WorkSource ws, int numNewResults) {
+    public void noteBleScanResults(final WorkSource ws, final int numNewResults) {
         enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteBluetoothScanResultsFromSourceLocked(ws, numNewResults);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteBluetoothScanResultsFromSourceLocked(ws, numNewResults,
+                            elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteWifiControllerActivity(WifiActivityEnergyInfo info) {
+    public void noteWifiControllerActivity(final WifiActivityEnergyInfo info) {
         enforceCallingPermission();
 
         if (info == null || !info.isValid()) {
@@ -1094,24 +1696,36 @@
             return;
         }
 
-        mStats.updateWifiState(info);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                mStats.updateWifiState(info, elapsedRealtime, uptime);
+            });
+        }
     }
 
     @Override
-    public void noteBluetoothControllerActivity(BluetoothActivityEnergyInfo info) {
+    public void noteBluetoothControllerActivity(final BluetoothActivityEnergyInfo info) {
         enforceCallingPermission();
         if (info == null || !info.isValid()) {
             Slog.e(TAG, "invalid bluetooth data given: " + info);
             return;
         }
 
-        synchronized (mStats) {
-            mStats.updateBluetoothStateLocked(info);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.updateBluetoothStateLocked(info, elapsedRealtime, uptime);
+                }
+            });
         }
     }
 
     @Override
-    public void noteModemControllerActivity(ModemActivityInfo info) {
+    public void noteModemControllerActivity(final ModemActivityInfo info) {
         enforceCallingPermission();
 
         if (info == null || !info.isValid()) {
@@ -1119,7 +1733,13 @@
             return;
         }
 
-        mStats.updateMobileRadioState(info);
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                mStats.updateMobileRadioState(info, elapsedRealtime, uptime);
+            });
+        }
     }
 
     public boolean isOnBattery() {
@@ -1132,32 +1752,43 @@
             final int chargeFullUAh, final long chargeTimeToFullSeconds) {
         enforceCallingPermission();
 
-        // BatteryService calls us here and we may update external state. It would be wrong
-        // to block such a low level service like BatteryService on external stats like WiFi.
-        mWorker.scheduleRunnable(() -> {
-            synchronized (mStats) {
-                final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status);
-                if (mStats.isOnBattery() == onBattery) {
-                    // The battery state has not changed, so we don't need to sync external
-                    // stats immediately.
-                    mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
-                            chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
-                    return;
-                }
-            }
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            final long currentTime = System.currentTimeMillis();
+            // We still schedule this task over the handler thread to make sure we've had
+            // all existing pending work handled before setting the battery state
+            mHandler.post(() -> {
+                // BatteryService calls us here and we may update external state. It would be wrong
+                // to block such a low level service like BatteryService on external stats like WiFi
+                mWorker.scheduleRunnable(() -> {
+                    synchronized (mStats) {
+                        final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status);
+                        if (mStats.isOnBattery() == onBattery) {
+                            // The battery state has not changed, so we don't need to sync external
+                            // stats immediately.
+                            mStats.setBatteryStateLocked(status, health, plugType, level, temp,
+                                    volt, chargeUAh, chargeFullUAh, chargeTimeToFullSeconds,
+                                    elapsedRealtime, uptime, currentTime);
+                            return;
+                        }
+                    }
 
-            // Sync external stats first as the battery has changed states. If we don't sync
-            // before changing the state, we may not collect the relevant data later.
-            // Order here is guaranteed since we're scheduling from the same thread and we are
-            // using a single threaded executor.
-            mWorker.scheduleSync("battery-state", BatteryExternalStatsWorker.UPDATE_ALL);
-            mWorker.scheduleRunnable(() -> {
-                synchronized (mStats) {
-                    mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
-                            chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
-                }
+                    // Sync external stats first as the battery has changed states. If we don't sync
+                    // before changing the state, we may not collect the relevant data later.
+                    // Order here is guaranteed since we're scheduling from the same thread and we
+                    // are using a single threaded executor.
+                    mWorker.scheduleSync("battery-state", BatteryExternalStatsWorker.UPDATE_ALL);
+                    mWorker.scheduleRunnable(() -> {
+                        synchronized (mStats) {
+                            mStats.setBatteryStateLocked(status, health, plugType, level, temp,
+                                    volt, chargeUAh, chargeFullUAh, chargeTimeToFullSeconds,
+                                    elapsedRealtime, uptime, currentTime);
+                        }
+                    });
+                });
             });
-        });
+        }
     }
 
     public long getAwakeTimeBattery() {
@@ -1205,8 +1836,12 @@
             try {
                 String reason;
                 while ((reason = waitWakeup()) != null) {
+                    // Wait for the completion of pending works if there is any
+                    awaitCompletion();
+
                     synchronized (mStats) {
-                        mStats.noteWakeupReasonLocked(reason);
+                        mStats.noteWakeupReasonLocked(reason,
+                                SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
                     }
                 }
             } catch (RuntimeException e) {
@@ -1273,12 +1908,16 @@
     }
 
     private void dumpSettings(PrintWriter pw) {
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
         synchronized (mStats) {
             mStats.dumpConstantsLocked(pw);
         }
     }
 
     private void dumpCpuStats(PrintWriter pw) {
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
         synchronized (mStats) {
             mStats.dumpCpuStatsLocked(pw);
         }
@@ -1292,14 +1931,20 @@
             return -1;
         }
         if ("full-wake-history".equals(args[i]) || "full-history".equals(args[i])) {
+            // Wait for the completion of pending works if there is any
+            awaitCompletion();
             synchronized (mStats) {
                 mStats.setRecordAllHistoryLocked(enable);
             }
         } else if ("no-auto-reset".equals(args[i])) {
+            // Wait for the completion of pending works if there is any
+            awaitCompletion();
             synchronized (mStats) {
                 mStats.setNoAutoReset(enable);
             }
         } else if ("pretend-screen-off".equals(args[i])) {
+            // Wait for the completion of pending works if there is any
+            awaitCompletion();
             synchronized (mStats) {
                 mStats.setPretendScreenOff(enable);
             }
@@ -1350,6 +1995,7 @@
                         return;
                     }
                     final long events = ParseUtils.parseLong(args[i], 0);
+                    awaitCompletion();
                     synchronized (mStats) {
                         mStats.createFakeHistoryEvents(events);
                         pw.println("Battery history create events started.");
@@ -1365,6 +2011,7 @@
                 } else if ("--daily".equals(arg)) {
                     flags |= BatteryStats.DUMP_DAILY_ONLY;
                 } else if ("--reset".equals(arg)) {
+                    awaitCompletion();
                     synchronized (mStats) {
                         mStats.resetAllStatsCmdLocked();
                         pw.println("Battery stats reset.");
@@ -1372,6 +2019,7 @@
                     }
                     mWorker.scheduleSync("dump", BatteryExternalStatsWorker.UPDATE_ALL);
                 } else if ("--write".equals(arg)) {
+                    awaitCompletion();
                     syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL);
                     synchronized (mStats) {
                         mStats.writeSyncLocked();
@@ -1379,12 +2027,14 @@
                         noOutput = true;
                     }
                 } else if ("--new-daily".equals(arg)) {
+                    awaitCompletion();
                     synchronized (mStats) {
                         mStats.recordDailyStatsLocked();
                         pw.println("New daily stats written.");
                         noOutput = true;
                     }
                 } else if ("--read-daily".equals(arg)) {
+                    awaitCompletion();
                     synchronized (mStats) {
                         mStats.readDailyStatsLocked();
                         pw.println("Last daily stats read.");
@@ -1441,6 +2091,7 @@
             if (BatteryStatsHelper.checkWifiOnly(mContext)) {
                 flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
             }
+            awaitCompletion();
             // Fetch data from external sources and update the BatteryStatsImpl object with them.
             syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL);
         } finally {
@@ -1489,6 +2140,7 @@
                 }
             }
             if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
+            awaitCompletion();
             synchronized (mStats) {
                 mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
                 if (writeData) {
@@ -1528,6 +2180,7 @@
                 }
             }
             if (DBG) Slog.d(TAG, "begin dumpCheckinLocked from UID " + Binder.getCallingUid());
+            awaitCompletion();
             synchronized (mStats) {
                 mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
                 if (writeData) {
@@ -1537,6 +2190,7 @@
             if (DBG) Slog.d(TAG, "end dumpCheckinLocked");
         } else {
             if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
+            awaitCompletion();
             synchronized (mStats) {
                 mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
                 if (writeData) {
@@ -1552,6 +2206,8 @@
      * @hide
      */
     public CellularBatteryStats getCellularBatteryStats() {
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
         synchronized (mStats) {
             return mStats.getCellularBatteryStats();
         }
@@ -1562,6 +2218,8 @@
      * @hide
      */
     public WifiBatteryStats getWifiBatteryStats() {
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
         synchronized (mStats) {
             return mStats.getWifiBatteryStats();
         }
@@ -1572,6 +2230,8 @@
      * @hide
      */
     public GpsBatteryStats getGpsBatteryStats() {
+        // Wait for the completion of pending works if there is any
+        awaitCompletion();
         synchronized (mStats) {
             return mStats.getGpsBatteryStats();
         }
@@ -1588,6 +2248,8 @@
         }
         long ident = Binder.clearCallingIdentity();
         try {
+            // Wait for the completion of pending works if there is any
+            awaitCompletion();
             if (shouldCollectExternalStats()) {
                 syncStats("get-health-stats-for-uids", BatteryExternalStatsWorker.UPDATE_ALL);
             }
@@ -1614,6 +2276,8 @@
         long ident = Binder.clearCallingIdentity();
         int i=-1;
         try {
+            // Wait for the completion of pending works if there is any
+            awaitCompletion();
             if (shouldCollectExternalStats()) {
                 syncStats("get-health-stats-for-uids", BatteryExternalStatsWorker.UPDATE_ALL);
             }
@@ -1685,4 +2349,124 @@
             Binder.restoreCallingIdentity(ident);
         }
     }
+
+    void updateForegroundTimeIfOnBattery(final String packageName, final int uid,
+            final long cpuTimeDiff) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                if (!isOnBattery()) {
+                    return;
+                }
+                synchronized (mStats) {
+                    final BatteryStatsImpl.Uid.Proc ps =
+                            mStats.getProcessStatsLocked(uid, packageName, elapsedRealtime, uptime);
+                    if (ps != null) {
+                        ps.addForegroundTimeLocked(cpuTimeDiff);
+                    }
+                }
+            });
+        }
+    }
+
+    void noteCurrentTimeChanged() {
+        synchronized (mLock) {
+            final long currentTime = System.currentTimeMillis();
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime);
+                }
+            });
+        }
+    }
+
+    void updateBatteryStatsOnActivityUsage(final String packageName, final String className,
+            final int uid, final int userId, final boolean resumed) {
+        synchronized (mLock) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
+                        uid, packageName, className,
+                        resumed
+                        ? FrameworkStatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND
+                        : FrameworkStatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
+                synchronized (mStats) {
+                    if (resumed) {
+                        mStats.noteActivityResumedLocked(uid, elapsedRealtime, uptime);
+                    } else {
+                        mStats.noteActivityPausedLocked(uid, elapsedRealtime, uptime);
+                    }
+                }
+            });
+        }
+    }
+
+    void noteProcessDied(final int uid, final int pid) {
+        synchronized (mLock) {
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.noteProcessDiedLocked(uid, pid);
+                }
+            });
+        }
+    }
+
+    void reportExcessiveCpu(final int uid, final String processName, final long uptimeSince,
+            long cputimeUsed) {
+        synchronized (mLock) {
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    mStats.reportExcessiveCpuLocked(uid, processName, uptimeSince, cputimeUsed);
+                }
+            });
+        }
+    }
+
+    void noteServiceStartRunning(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+        synchronized (mLock) {
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    stats.startRunningLocked(uptime);
+                }
+            });
+        }
+    }
+
+    void noteServiceStopRunning(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+        synchronized (mLock) {
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    stats.stopRunningLocked(uptime);
+                }
+            });
+        }
+    }
+
+    void noteServiceStartLaunch(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+        synchronized (mLock) {
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    stats.startLaunchedLocked(uptime);
+                }
+            });
+        }
+    }
+
+    void noteServiceStopLaunch(final BatteryStatsImpl.Uid.Pkg.Serv stats) {
+        synchronized (mLock) {
+            final long uptime = SystemClock.uptimeMillis();
+            mHandler.post(() -> {
+                synchronized (mStats) {
+                    stats.stopLaunchedLocked(uptime);
+                }
+            });
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6948f90..93105da 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -964,7 +964,7 @@
                 + mParallelBroadcasts.size() + " parallel broadcasts; "
                 + mDispatcher.describeStateLocked());
 
-        mService.updateCpuStats();
+        mService.updateCpuStatsLocked();
 
         if (fromMsg) {
             mBroadcastsScheduled = false;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c5047e5..9660389 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -479,6 +479,21 @@
     private static native void enableFreezerInternal(boolean enable);
 
     /**
+     * Informs binder that a process is about to be frozen. If freezer is enabled on a process via
+     * this method, this method will synchronously dispatch all pending transactions to the
+     * specified pid. This method will not add significant latencies when unfreezing.
+     * After freezing binder calls, binder will block all transaction to the frozen pid, and return
+     * an error to the sending process.
+     *
+     * @param pid the target pid for which binder transactions are to be frozen
+     * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
+     * binder for the specificed pid.
+     *
+     * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
+     */
+    private static native void freezeBinder(int pid, boolean freeze);
+
+    /**
      * Determines whether the freezer is supported by this system
      */
     public static boolean isFreezerSupported() {
@@ -727,6 +742,13 @@
         }
 
         if (!app.frozen) {
+            try {
+                freezeBinder(app.pid, false);
+            } catch (RuntimeException e) {
+                // TODO: it might be preferable to kill the target pid in this case
+                Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName);
+            }
+
             if (DEBUG_FREEZER) {
                 Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
             }
@@ -1039,6 +1061,14 @@
                     return;
                 }
 
+                try {
+                    freezeBinder(pid, true);
+                } catch (RuntimeException e) {
+                    // TODO: it might be preferable to kill the target pid in this case
+                    Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+                    return;
+                }
+
                 if (pid == 0 || proc.frozen) {
                     // Already frozen or not a real process, either one being
                     // launched or one being killed
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index fc33031..c313eb5 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -66,9 +66,9 @@
 import static com.android.server.am.ActivityManagerService.TAG_LRU;
 import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ;
 import static com.android.server.am.ActivityManagerService.TAG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerService.TAG_PSS;
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST;
+import static com.android.server.am.AppProfiler.TAG_PSS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 
 import android.app.ActivityManager;
@@ -83,7 +83,6 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
-import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManagerInternal;
@@ -102,7 +101,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -819,7 +817,7 @@
         }
 
         if (allChanged) {
-            mService.requestPssAllProcsLocked(now, false,
+            mService.mAppProfiler.requestPssAllProcsLocked(now, false,
                     mService.mProcessStats.isMemFactorLowered());
         }
 
@@ -1075,7 +1073,7 @@
 
         mProcessList.incrementProcStateSeqAndNotifyAppsLocked(activeUids);
 
-        return mService.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+        return mService.mAppProfiler.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
     }
 
     private void updateAppUidRecLocked(ProcessRecord app) {
@@ -2176,7 +2174,7 @@
                     // normally be a B service, but if we are low on RAM and it
                     // is large we want to force it down since we would prefer to
                     // keep launcher over it.
-                    if (mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                    if (!mService.mAppProfiler.isLastMemoryLevelNormal()
                             && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
                         app.serviceHighRam = true;
                         app.serviceb = true;
@@ -2493,45 +2491,16 @@
         }
         if (app.setProcState == PROCESS_STATE_NONEXISTENT
                 || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
-            if (false && mService.mTestPssMode
-                    && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
-                // Experimental code to more aggressively collect pss while
-                // running test...  the problem is that this tends to collect
-                // the data right when a process is transitioning between process
-                // states, which will tend to give noisy data.
-                long start = SystemClock.uptimeMillis();
-                long startTime = SystemClock.currentThreadTimeMillis();
-                long pss = Debug.getPss(app.pid, mTmpLong, null);
-                long endTime = SystemClock.currentThreadTimeMillis();
-                mService.recordPssSampleLocked(app, app.getCurProcState(), pss,
-                        mTmpLong[0], mTmpLong[1], mTmpLong[2],
-                        ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
-                mService.mPendingPssProcesses.remove(app);
-                Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
-                        + " to " + app.getCurProcState() + ": "
-                        + (SystemClock.uptimeMillis()-start) + "ms");
-            }
             app.lastStateTime = now;
-            app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
-                    app.procStateMemTracker, mService.mTestPssMode,
-                    mService.mAtmInternal.isSleeping(), now);
-            if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
-                    + ProcessList.makeProcStateString(app.setProcState) + " to "
-                    + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
-                    + (app.nextPssTime-now) + ": " + app);
-        } else {
-            if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
-                    && now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
-                    mService.mTestPssMode)))) {
-                if (mService.requestPssLocked(app, app.setProcState)) {
-                    app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
-                            app.procStateMemTracker, mService.mTestPssMode,
-                            mService.mAtmInternal.isSleeping(), now);
-                }
-            } else if (false && DEBUG_PSS) {
-                Slog.d(TAG_PSS,
-                        "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
+            mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
+            if (DEBUG_PSS) {
+                Slog.d(TAG_PSS, "Process state change from "
+                        + ProcessList.makeProcStateString(app.setProcState) + " to "
+                        + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+                        + (app.nextPssTime - now) + ": " + app);
             }
+        } else {
+            mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, false);
         }
         if (app.setProcState != app.getCurProcState()) {
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 87898d8..ced2f0f 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -48,8 +48,8 @@
 import static com.android.server.am.ActivityManagerService.TAG_LRU;
 import static com.android.server.am.ActivityManagerService.TAG_NETWORK;
 import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
-import static com.android.server.am.ActivityManagerService.TAG_PSS;
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
+import static com.android.server.am.AppProfiler.TAG_PSS;
 
 import android.app.ActivityManager;
 import android.app.ActivityThread;
@@ -1323,19 +1323,6 @@
         return test ? PSS_TEST_MIN_TIME_FROM_STATE_CHANGE : PSS_MIN_TIME_FROM_STATE_CHANGE;
     }
 
-    public static void commitNextPssTime(ProcStateMemTracker tracker) {
-        if (tracker.mPendingMemState >= 0) {
-            tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
-            tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
-            tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
-            tracker.mPendingMemState = -1;
-        }
-    }
-
-    public static void abortNextPssTime(ProcStateMemTracker tracker) {
-        tracker.mPendingMemState = -1;
-    }
-
     public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
             boolean sleeping, long now) {
         boolean first;
@@ -1547,7 +1534,7 @@
                     ApplicationExitInfo.SUBREASON_LARGE_CACHED,
                     true);
         } else if (proc != null && !keepIfLarge
-                && mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                && !mService.mAppProfiler.isLastMemoryLevelNormal()
                 && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
             if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
                     .lastCachedPss);
@@ -1772,7 +1759,7 @@
         mService.mProcessesOnHold.remove(app);
 
         checkSlow(startTime, "startProcess: starting to update cpu stats");
-        mService.updateCpuStats();
+        mService.updateCpuStatsLocked();
         checkSlow(startTime, "startProcess: done updating cpu stats");
 
         try {
@@ -2422,7 +2409,8 @@
             ProcessList.killProcessGroup(app.uid, app.pid);
             checkSlow(startTime, "startProcess: done killing old proc");
 
-            if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+            if (!app.killed
+                    || mService.mAppProfiler.isLastMemoryLevelNormal()
                     || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
                     || app.lastCachedPss < getCachedRestoreThresholdKb()) {
                 // Throw a wtf if it's not killed, or killed but not because the system was in
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 14bc6cb..f1e12c5 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1422,12 +1422,7 @@
     @Override
     public void clearProfilerIfNeeded() {
         synchronized (mService) {
-            if (mService.mProfileData.getProfileProc() == null
-                    || mService.mProfileData.getProfilerInfo() == null
-                    || mService.mProfileData.getProfileProc() != this) {
-                return;
-            }
-            mService.clearProfilerLocked();
+            mService.mAppProfiler.clearProfilerLocked();
         }
     }
 
@@ -1483,7 +1478,7 @@
      */
     @Override
     public long getCpuTime() {
-        return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
+        return mService.mAppProfiler.getCpuTimeForPid(pid);
     }
 
     @Override
@@ -1492,7 +1487,7 @@
         synchronized (mService) {
             waitingToKill = null;
             if (setProfileProc) {
-                mService.mProfileData.setProfileProc(this);
+                mService.mAppProfiler.setProfileProcLocked(this);
             }
             if (packageName != null) {
                 addPackage(packageName, versionCode, mService.mProcessStats);
@@ -1566,7 +1561,7 @@
     /** Non-private access is for tests only. */
     @VisibleForTesting
     boolean isMonitorCpuUsage() {
-        return mService.MONITOR_CPU_USAGE;
+        return mService.mAppProfiler.MONITOR_CPU_USAGE;
     }
 
     void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
@@ -1701,9 +1696,7 @@
 
         if (isMonitorCpuUsage()) {
             mService.updateCpuStatsNow();
-            synchronized (mService.mProcessCpuTracker) {
-                report.append(mService.mProcessCpuTracker.printCurrentState(anrTime));
-            }
+            mService.mAppProfiler.printCurrentCpuState(report, anrTime);
             info.append(processCpuTracker.printCurrentLoad());
             info.append(report);
         }
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 4e8c386..d925def 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -314,7 +314,8 @@
     private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
         mAm.mHandler.post(() -> {
             synchronized (mAm) {
-                mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered);
+                mAm.mAppProfiler.requestPssAllProcsLocked(
+                        SystemClock.uptimeMillis(), always, memLowered);
             }
         });
     }
@@ -1056,17 +1057,13 @@
                         }
                     }
                 } else if ("--start-testing".equals(arg)) {
-                    synchronized (mAm) {
-                        mAm.setTestPssMode(true);
-                        pw.println("Started high frequency sampling.");
-                        quit = true;
-                    }
+                    mAm.mAppProfiler.setTestPssMode(true);
+                    pw.println("Started high frequency sampling.");
+                    quit = true;
                 } else if ("--stop-testing".equals(arg)) {
-                    synchronized (mAm) {
-                        mAm.setTestPssMode(false);
-                        pw.println("Stopped high frequency sampling.");
-                        quit = true;
-                    }
+                    mAm.mAppProfiler.setTestPssMode(false);
+                    pw.println("Stopped high frequency sampling.");
+                    quit = true;
                 } else if ("--pretend-screen-on".equals(arg)) {
                     synchronized (mLock) {
                         mInjectedScreenState = true;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 5b12c8c..55e0f2e 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -149,7 +149,7 @@
 
     // allow the service becomes foreground service? Service started from background may not be
     // allowed to become a foreground service.
-    boolean mAllowStartForeground;
+    @ActiveServices.FgsFeatureRetCode int mAllowStartForeground;
     String mInfoAllowStartForeground;
     boolean mLoggedInfoAllowStartForeground;
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index dfe8af1..b56b09d 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2206,6 +2206,7 @@
             updatePermissionRevokedCompat(uid, code, mode);
         }
 
+        int previousMode;
         synchronized (this) {
             final int defaultMode = AppOpsManager.opToDefaultMode(code);
 
@@ -2214,12 +2215,14 @@
                 if (mode == defaultMode) {
                     return;
                 }
+                previousMode = AppOpsManager.MODE_DEFAULT;
                 uidState = new UidState(uid);
                 uidState.opModes = new SparseIntArray();
                 uidState.opModes.put(code, mode);
                 mUidStates.put(uid, uidState);
                 scheduleWriteLocked();
             } else if (uidState.opModes == null) {
+                previousMode = AppOpsManager.MODE_DEFAULT;
                 if (mode != defaultMode) {
                     uidState.opModes = new SparseIntArray();
                     uidState.opModes.put(code, mode);
@@ -2229,6 +2232,7 @@
                 if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
                     return;
                 }
+                previousMode = uidState.opModes.get(code);
                 if (mode == defaultMode) {
                     uidState.opModes.delete(code);
                     if (uidState.opModes.size() <= 0) {
@@ -2243,7 +2247,7 @@
         }
 
         notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
-        notifyOpChangedSync(code, uid, null, mode);
+        notifyOpChangedSync(code, uid, null, mode, previousMode);
     }
 
     /**
@@ -2420,11 +2424,12 @@
         }
     }
 
-    private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) {
+    private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode,
+            int previousMode) {
         final StorageManagerInternal storageManagerInternal =
                 LocalServices.getService(StorageManagerInternal.class);
         if (storageManagerInternal != null) {
-            storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode);
+            storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode, previousMode);
         }
     }
 
@@ -2458,11 +2463,13 @@
             return;
         }
 
+        int previousMode = AppOpsManager.MODE_DEFAULT;
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
             Op op = getOpLocked(code, uid, packageName, null, bypass, true);
             if (op != null) {
                 if (op.mode != mode) {
+                    previousMode = op.mode;
                     op.mode = mode;
                     if (uidState != null) {
                         uidState.evalForegroundOps(mOpModeWatchers);
@@ -2499,7 +2506,7 @@
                     this, repCbs, code, uid, packageName));
         }
 
-        notifyOpChangedSync(code, uid, packageName, mode);
+        notifyOpChangedSync(code, uid, packageName, mode, previousMode);
     }
 
     private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code,
@@ -2542,7 +2549,7 @@
     }
 
     private static ArrayList<ChangeRec> addChange(ArrayList<ChangeRec> reports,
-            int op, int uid, String packageName) {
+            int op, int uid, String packageName, int previousMode) {
         boolean duplicate = false;
         if (reports == null) {
             reports = new ArrayList<>();
@@ -2557,7 +2564,7 @@
             }
         }
         if (!duplicate) {
-            reports.add(new ChangeRec(op, uid, packageName));
+            reports.add(new ChangeRec(op, uid, packageName, previousMode));
         }
 
         return reports;
@@ -2565,7 +2572,7 @@
 
     private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks(
             HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks,
-            int op, int uid, String packageName, ArraySet<ModeCallback> cbs) {
+            int op, int uid, String packageName, int previousMode, ArraySet<ModeCallback> cbs) {
         if (cbs == null) {
             return callbacks;
         }
@@ -2576,7 +2583,7 @@
         for (int i=0; i<N; i++) {
             ModeCallback cb = cbs.valueAt(i);
             ArrayList<ChangeRec> reports = callbacks.get(cb);
-            ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName);
+            ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName, previousMode);
             if (changed != reports) {
                 callbacks.put(cb, changed);
             }
@@ -2588,11 +2595,13 @@
         final int op;
         final int uid;
         final String pkg;
+        final int previous_mode;
 
-        ChangeRec(int _op, int _uid, String _pkg) {
+        ChangeRec(int _op, int _uid, String _pkg, int _previous_mode) {
             op = _op;
             uid = _uid;
             pkg = _pkg;
+            previous_mode = _previous_mode;
         }
     }
 
@@ -2628,18 +2637,19 @@
                     for (int j = uidOpCount - 1; j >= 0; j--) {
                         final int code = opModes.keyAt(j);
                         if (AppOpsManager.opAllowsReset(code)) {
+                            int previousMode = opModes.valueAt(j);
                             opModes.removeAt(j);
                             if (opModes.size() <= 0) {
                                 uidState.opModes = null;
                             }
                             for (String packageName : getPackagesForUid(uidState.uid)) {
                                 callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
-                                        mOpModeWatchers.get(code));
+                                        previousMode, mOpModeWatchers.get(code));
                                 callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
-                                        mPackageModeWatchers.get(packageName));
+                                        previousMode, mPackageModeWatchers.get(packageName));
 
                                 allChanges = addChange(allChanges, code, uidState.uid,
-                                        packageName);
+                                        packageName, previousMode);
                             }
                         }
                     }
@@ -2670,16 +2680,18 @@
                         Op curOp = pkgOps.valueAt(j);
                         if (AppOpsManager.opAllowsReset(curOp.op)
                                 && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
+                            int previousMode = curOp.mode;
                             curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
                             changed = true;
                             uidChanged = true;
                             final int uid = curOp.uidState.uid;
                             callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
-                                    mOpModeWatchers.get(curOp.op));
+                                    previousMode, mOpModeWatchers.get(curOp.op));
                             callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
-                                    mPackageModeWatchers.get(packageName));
+                                    previousMode, mPackageModeWatchers.get(packageName));
 
-                            allChanges = addChange(allChanges, curOp.op, uid, packageName);
+                            allChanges = addChange(allChanges, curOp.op, uid, packageName,
+                                    previousMode);
                             curOp.removeAttributionsWithNoTime();
                             if (curOp.mAttributions.isEmpty()) {
                                 pkgOps.removeAt(j);
@@ -2720,7 +2732,7 @@
             for (int i = 0; i < numChanges; i++) {
                 ChangeRec change = allChanges.get(i);
                 notifyOpChangedSync(change.op, change.uid, change.pkg,
-                        AppOpsManager.opToDefaultMode(change.op));
+                        AppOpsManager.opToDefaultMode(change.op), change.previous_mode);
             }
         }
     }
@@ -3926,7 +3938,7 @@
 
             if (!isAttributionTagValid) {
                 String msg = "attributionTag " + attributionTag + " not declared in"
-                        + "manifest of " + packageName;
+                        + " manifest of " + packageName;
                 try {
                     if (mPlatformCompat.isChangeEnabledByPackageName(
                             SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index d2c35fe..1613d3b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -282,8 +282,10 @@
             @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
         final boolean supportsSelfIllumination = context.getResources()
                 .getBoolean(R.bool.config_faceAuthSupportsSelfIllumination);
+        final int maxTemplatesAllowed = context.getResources()
+                .getInteger(R.integer.config_faceMaxTemplatesPerUser);
         mFaceSensorProperties = new FaceSensorProperties(sensorId, false /* supportsFaceDetect */,
-                supportsSelfIllumination);
+                supportsSelfIllumination, maxTemplatesAllowed);
         mContext = context;
         mSensorId = sensorId;
         mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
index 3754bd7..7e62329 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -45,6 +45,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
+import com.android.internal.R;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
@@ -338,8 +339,10 @@
                         : FingerprintSensorProperties.TYPE_REAR;
         // resetLockout is controlled by the framework, so hardwareAuthToken is not required
         final boolean resetLockoutRequiresHardwareAuthToken = false;
+        final int maxTemplatesAllowed = mContext.getResources()
+                .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
         mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType,
-                resetLockoutRequiresHardwareAuthToken);
+                resetLockoutRequiresHardwareAuthToken, maxTemplatesAllowed);
     }
 
     static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
index 1a6ad46..7cab2b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
@@ -35,6 +35,7 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.R;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.ClientMonitor;
@@ -408,8 +409,11 @@
         mHandler = handler;
         // resetLockout is controlled by the framework, so hardwareAuthToken is not required
         final boolean resetLockoutRequiresHardwareAuthToken = false;
+        final int maxTemplatesAllowed = mContext.getResources()
+                .getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
         mSensorProperties = new FingerprintSensorProperties(sensorId,
-                FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken);
+                FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken,
+                maxTemplatesAllowed);
         mMockHalResultController = controller;
         mUserHasTrust = new SparseBooleanArray();
         mTrustManager = context.getSystemService(TrustManager.class);
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 92fce8a..c7de8d3 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -107,17 +107,23 @@
     }
 
     /**
-     * Internal version of the above method. Does not perform costly permission check.
+     * Internal version of the above method, without logging. Does not perform costly permission
+     * check.
+     * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
+     */
+    public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
+        return mCompatConfig.isChangeEnabled(changeId, appInfo);
+    }
+
+    /**
+     * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly
+     * permission check.
      */
     public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
-        if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
-            reportChange(changeId, appInfo.uid,
-                    ChangeReporter.STATE_ENABLED);
-            return true;
-        }
+        boolean value = isChangeEnabledInternalNoLogging(changeId, appInfo);
         reportChange(changeId, appInfo.uid,
-                ChangeReporter.STATE_DISABLED);
-        return false;
+                value ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
+        return value;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 36d69c9..f42e18a 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,8 +17,8 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.Context;
@@ -846,7 +846,7 @@
             public void run() {
                 try {
                     // The foreground app is the top activity of the focused tasks stack.
-                    final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+                    final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo();
                     if (info == null || info.topActivity == null) {
                         return;
                     }
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index eea1980..9e82d4f 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -20,6 +20,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -377,14 +378,14 @@
         }
 
         try {
-            final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
-            if (focusedStack != null && focusedStack.topActivity != null) {
-                builder.setUserId(focusedStack.userId);
-                builder.setPackageName(focusedStack.topActivity.getPackageName());
+            final RootTaskInfo focusedTask = mInjector.getFocusedStack();
+            if (focusedTask != null && focusedTask.topActivity != null) {
+                builder.setUserId(focusedTask.userId);
+                builder.setPackageName(focusedTask.topActivity.getPackageName());
             } else {
                 // Ignore the event because we can't determine user / package.
                 if (DEBUG) {
-                    Slog.d(TAG, "Ignoring event due to null focusedStack.");
+                    Slog.d(TAG, "Ignoring event due to null focusedTask.");
                 }
                 return;
             }
@@ -1104,8 +1105,8 @@
             }
         }
 
-        public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
-            return ActivityTaskManager.getService().getFocusedStackInfo();
+        public RootTaskInfo getFocusedStack() throws RemoteException {
+            return ActivityTaskManager.getService().getFocusedRootTaskInfo();
         }
 
         public void scheduleIdleJob(Context context) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 3c05080..cc6687f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -869,10 +869,11 @@
         }
 
         private void updateRefreshRateSettingLocked() {
-            float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
-                    Settings.System.MIN_REFRESH_RATE, 0f);
-            float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
-                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
+            final ContentResolver cr = mContext.getContentResolver();
+            float minRefreshRate = Settings.System.getFloatForUser(cr,
+                    Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+            float peakRefreshRate = Settings.System.getFloatForUser(cr,
+                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
         }
 
@@ -1301,8 +1302,9 @@
         }
         // TODO: brightnessfloat: make it use float not int
         private void onBrightnessChangedLocked() {
-            int brightness = Settings.System.getInt(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS, -1);
+            final ContentResolver cr = mContext.getContentResolver();
+            int brightness = Settings.System.getIntForUser(cr,
+                    Settings.System.SCREEN_BRIGHTNESS, -1, cr.getUserId());
 
             Vote vote = null;
             boolean insideZone = isInsideZone(brightness, mAmbientLux);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 813def4..6c14b2c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -222,7 +222,7 @@
     private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
     private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
             int displayId, boolean isGestureMonitor);
-    private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
+    private static native void nativeUnregisterInputChannel(long ptr, IBinder connectionToken);
     private static native void nativePilferPointers(long ptr, IBinder token);
     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
     private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
@@ -581,17 +581,17 @@
 
     /**
      * Unregisters an input channel.
-     * @param inputChannel The input channel to unregister.
+     * @param connectionToken The input channel to unregister.
      */
-    public void unregisterInputChannel(InputChannel inputChannel) {
-        if (inputChannel == null) {
-            throw new IllegalArgumentException("inputChannel must not be null.");
+    public void unregisterInputChannel(IBinder connectionToken) {
+        if (connectionToken == null) {
+            throw new IllegalArgumentException("connectionToken must not be null.");
         }
         synchronized (mGestureMonitorPidsLock) {
-            mGestureMonitorPidsByToken.remove(inputChannel.getToken());
+            mGestureMonitorPidsByToken.remove(connectionToken);
         }
 
-        nativeUnregisterInputChannel(mPtr, inputChannel);
+        nativeUnregisterInputChannel(mPtr, connectionToken);
     }
 
     /**
@@ -2455,7 +2455,7 @@
 
         @Override
         public void dispose() {
-            nativeUnregisterInputChannel(mPtr, mInputChannel);
+            nativeUnregisterInputChannel(mPtr, mInputChannel.getToken());
             mInputChannel.dispose();
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5bc6e23..7401403 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1684,8 +1684,10 @@
                 final IntArray userIds = mUserProfiles.getCurrentProfileIds();
 
                 for (int i = 0; i < userIds.size(); i++) {
-                    mArchive.updateHistoryEnabled(userIds.get(i), Settings.Secure.getInt(resolver,
-                            Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1);
+                    mArchive.updateHistoryEnabled(userIds.get(i),
+                            Settings.Secure.getIntForUser(resolver,
+                                    Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0,
+                                    userIds.get(i)) == 1);
                 }
             }
             if (uri == null || NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI.equals(uri)) {
@@ -1956,6 +1958,13 @@
             }
 
             @Override
+            void onConsolidatedPolicyChanged() {
+                Binder.withCleanCallingIdentity(() -> {
+                    mRankingHandler.requestSort();
+                });
+            }
+
+            @Override
             void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
                 Binder.withCleanCallingIdentity(() -> {
                     Intent intent = new Intent(ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED);
@@ -7148,9 +7157,10 @@
     }
 
     protected void playInCallNotification() {
+        final ContentResolver cr = getContext().getContentResolver();
         if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
-                && Settings.Secure.getInt(getContext().getContentResolver(),
-                Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1) != 0) {
+                && Settings.Secure.getIntForUser(cr,
+                Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1, cr.getUserId()) != 0) {
             new Thread() {
                 @Override
                 public void run() {
@@ -8462,7 +8472,10 @@
         if (Notification.CATEGORY_CAR_EMERGENCY.equals(notification.category)
                 || Notification.CATEGORY_CAR_WARNING.equals(notification.category)
                 || Notification.CATEGORY_CAR_INFORMATION.equals(notification.category)) {
-                    checkCallerIsSystem();
+            getContext().enforceCallingPermission(
+                    android.Manifest.permission.SEND_CATEGORY_CAR_NOTIFICATIONS,
+                    String.format("Notification category %s restricted",
+                            notification.category));
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index bae02ac..cff4f07 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -469,6 +469,69 @@
         pw.println(prefix + "key=" + getSbn().getKey());
         pw.println(prefix + "seen=" + mStats.hasSeen());
         pw.println(prefix + "groupKey=" + getGroupKey());
+        pw.println(prefix + "notification=");
+        dumpNotification(pw, prefix + prefix, notification, redact);
+        pw.println(prefix + "publicNotification=");
+        dumpNotification(pw, prefix + prefix, notification.publicVersion, redact);
+        pw.println(prefix + "stats=" + stats.toString());
+        pw.println(prefix + "mContactAffinity=" + mContactAffinity);
+        pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
+        pw.println(prefix + "mPackagePriority=" + mPackagePriority);
+        pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
+        pw.println(prefix + "mSystemImportance="
+                + NotificationListenerService.Ranking.importanceToString(mSystemImportance));
+        pw.println(prefix + "mAsstImportance="
+                + NotificationListenerService.Ranking.importanceToString(mAssistantImportance));
+        pw.println(prefix + "mImportance="
+                + NotificationListenerService.Ranking.importanceToString(mImportance));
+        pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation());
+        pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
+        pw.println(prefix + "mIntercept=" + mIntercept);
+        pw.println(prefix + "mHidden==" + mHidden);
+        pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
+        pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
+        pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
+        pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
+        pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
+        pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
+        pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
+        if (mPreChannelsNotification) {
+            pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
+                    notification.defaults, notification.flags));
+            pw.println(prefix + "n.sound=" + notification.sound);
+            pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
+            pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
+            pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
+                    notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
+            pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
+        }
+        pw.println(prefix + "mSound= " + mSound);
+        pw.println(prefix + "mVibration= " + mVibration);
+        pw.println(prefix + "mAttributes= " + mAttributes);
+        pw.println(prefix + "mLight= " + mLight);
+        pw.println(prefix + "mShowBadge=" + mShowBadge);
+        pw.println(prefix + "mColorized=" + notification.isColorized());
+        pw.println(prefix + "mAllowBubble=" + mAllowBubble);
+        pw.println(prefix + "isBubble=" + notification.isBubbleNotification());
+        pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
+        pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
+        if (getPeopleOverride() != null) {
+            pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
+        }
+        if (getSnoozeCriteria() != null) {
+            pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
+        }
+        pw.println(prefix + "mAdjustments=" + mAdjustments);
+        pw.println(prefix + "shortcut=" + notification.getShortcutId()
+                + " found valid? " + (mShortcutInfo != null));
+    }
+
+    private void dumpNotification(PrintWriter pw, String prefix, Notification notification,
+            boolean redact) {
+        if (notification == null) {
+            pw.println(prefix + "None");
+            return;
+        }
         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
         pw.println(prefix + "contentIntent=" + notification.contentIntent);
         pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
@@ -545,57 +608,6 @@
             }
             pw.println(prefix + "}");
         }
-        pw.println(prefix + "stats=" + stats.toString());
-        pw.println(prefix + "mContactAffinity=" + mContactAffinity);
-        pw.println(prefix + "mRecentlyIntrusive=" + mRecentlyIntrusive);
-        pw.println(prefix + "mPackagePriority=" + mPackagePriority);
-        pw.println(prefix + "mPackageVisibility=" + mPackageVisibility);
-        pw.println(prefix + "mSystemImportance="
-                + NotificationListenerService.Ranking.importanceToString(mSystemImportance));
-        pw.println(prefix + "mAsstImportance="
-                + NotificationListenerService.Ranking.importanceToString(mAssistantImportance));
-        pw.println(prefix + "mImportance="
-                + NotificationListenerService.Ranking.importanceToString(mImportance));
-        pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation());
-        pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
-        pw.println(prefix + "mIntercept=" + mIntercept);
-        pw.println(prefix + "mHidden==" + mHidden);
-        pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
-        pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
-        pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
-        pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
-        pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
-        pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
-        pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
-        if (mPreChannelsNotification) {
-            pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
-                    notification.defaults, notification.flags));
-            pw.println(prefix + "n.sound=" + notification.sound);
-            pw.println(prefix + "n.audioStreamType=" + notification.audioStreamType);
-            pw.println(prefix + "n.audioAttributes=" + notification.audioAttributes);
-            pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
-                    notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
-            pw.println(prefix + "vibrate=" + Arrays.toString(notification.vibrate));
-        }
-        pw.println(prefix + "mSound= " + mSound);
-        pw.println(prefix + "mVibration= " + mVibration);
-        pw.println(prefix + "mAttributes= " + mAttributes);
-        pw.println(prefix + "mLight= " + mLight);
-        pw.println(prefix + "mShowBadge=" + mShowBadge);
-        pw.println(prefix + "mColorized=" + notification.isColorized());
-        pw.println(prefix + "mAllowBubble=" + mAllowBubble);
-        pw.println(prefix + "isBubble=" + notification.isBubbleNotification());
-        pw.println(prefix + "mIsInterruptive=" + mIsInterruptive);
-        pw.println(prefix + "effectiveNotificationChannel=" + getChannel());
-        if (getPeopleOverride() != null) {
-            pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride()));
-        }
-        if (getSnoozeCriteria() != null) {
-            pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
-        }
-        pw.println(prefix + "mAdjustments=" + mAdjustments);
-        pw.println(prefix + "shortcut=" + notification.getShortcutId()
-                + " found valid? " + (mShortcutInfo != null));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5417275..13cd6e5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -417,7 +417,9 @@
             if (mConfig == null) return;
 
             newConfig = mConfig.copy();
-            setAutomaticZenRuleStateLocked(newConfig, newConfig.automaticRules.get(id), condition);
+            ArrayList<ZenRule> rules = new ArrayList<>();
+            rules.add(newConfig.automaticRules.get(id));
+            setAutomaticZenRuleStateLocked(newConfig, rules, condition);
         }
     }
 
@@ -428,31 +430,34 @@
             newConfig = mConfig.copy();
 
             setAutomaticZenRuleStateLocked(newConfig,
-                    findMatchingRule(newConfig, ruleDefinition, condition),
+                    findMatchingRules(newConfig, ruleDefinition, condition),
                     condition);
         }
     }
 
-    private void setAutomaticZenRuleStateLocked(ZenModeConfig config, ZenRule rule,
+    private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
             Condition condition) {
-        if (rule == null) return;
+        if (rules == null || rules.isEmpty()) return;
 
-        rule.condition = condition;
-        updateSnoozing(rule);
-        setConfigLocked(config, rule.component, "conditionChanged");
+        for (ZenRule rule : rules) {
+            rule.condition = condition;
+            updateSnoozing(rule);
+            setConfigLocked(config, rule.component, "conditionChanged");
+        }
     }
 
-    private ZenRule findMatchingRule(ZenModeConfig config, Uri id, Condition condition) {
+    private List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id, Condition condition) {
+        List<ZenRule> matchingRules= new ArrayList<>();
         if (ruleMatches(id, condition, config.manualRule)) {
-            return config.manualRule;
+            matchingRules.add(config.manualRule);
         } else {
             for (ZenRule automaticRule : config.automaticRules.values()) {
                 if (ruleMatches(id, condition, automaticRule)) {
-                    return automaticRule;
+                    matchingRules.add(automaticRule);
                 }
             }
         }
-        return null;
+        return matchingRules;
     }
 
     private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 8c03c6c..8121a43e9 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -26,6 +26,7 @@
 import android.text.TextUtils;
 import android.util.Pair;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 
@@ -66,7 +67,7 @@
 
         String actorNamespace = actorUri.getAuthority();
         Map<String, String> namespace = namedActors.get(actorNamespace);
-        if (namespace == null) {
+        if (ArrayUtils.isEmpty(namespace)) {
             return Pair.create(null, ActorState.MISSING_NAMESPACE);
         }
 
@@ -102,21 +103,32 @@
      * See {@link OverlayActorEnforcer} class comment for actor requirements.
      * @return true if the actor is allowed to act on the target overlayInfo
      */
-    private ActorState isAllowedActor(String methodName, OverlayInfo overlayInfo,
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public ActorState isAllowedActor(String methodName, OverlayInfo overlayInfo,
             int callingUid, int userId) {
+        // Checked first to avoid package not found errors, which are ignored for calls from shell
         switch (callingUid) {
             case Process.ROOT_UID:
             case Process.SYSTEM_UID:
                 return ActorState.ALLOWED;
         }
 
+        final String targetPackageName = overlayInfo.targetPackageName;
+        final PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
+        if (targetPkgInfo == null) {
+            return ActorState.TARGET_NOT_FOUND;
+        }
+
+        if ((targetPkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+            return ActorState.ALLOWED;
+        }
+
         String[] callingPackageNames = mPackageManager.getPackagesForUid(callingUid);
         if (ArrayUtils.isEmpty(callingPackageNames)) {
             return ActorState.NO_PACKAGES_FOR_UID;
         }
 
         // A target is always an allowed actor for itself
-        String targetPackageName = overlayInfo.targetPackageName;
         if (ArrayUtils.contains(callingPackageNames, targetPackageName)) {
             return ActorState.ALLOWED;
         }
@@ -149,7 +161,7 @@
             targetOverlayable = mPackageManager.getOverlayableForTarget(targetPackageName,
                     targetOverlayableName, userId);
         } catch (IOException e) {
-            return ActorState.UNABLE_TO_GET_TARGET;
+            return ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE;
         }
 
         if (targetOverlayable == null) {
@@ -189,7 +201,7 @@
         }
 
         // Currently only pre-installed apps can be actors
-        if (!appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) {
+        if (!appInfo.isSystemApp()) {
             return ActorState.ACTOR_NOT_PREINSTALLED;
         }
 
@@ -203,22 +215,25 @@
     /**
      * For easier logging/debugging, a set of all possible failure/success states when running
      * enforcement.
+     *
+     * The ordering of this enum should be maintained in the order that cases are checked in code,
+     * as this ordering is used inside OverlayActorEnforcerTests.
      */
     public enum ActorState {
-        ALLOWED,
-        INVALID_ACTOR,
-        MISSING_NAMESPACE,
-        MISSING_PACKAGE,
-        MISSING_APP_INFO,
-        ACTOR_NOT_PREINSTALLED,
+        TARGET_NOT_FOUND,
         NO_PACKAGES_FOR_UID,
-        MISSING_ACTOR_NAME,
-        ERROR_READING_OVERLAYABLE,
         MISSING_TARGET_OVERLAYABLE_NAME,
+        MISSING_LEGACY_PERMISSION,
+        ERROR_READING_OVERLAYABLE,
+        UNABLE_TO_GET_TARGET_OVERLAYABLE,
         MISSING_OVERLAYABLE,
         INVALID_OVERLAYABLE_ACTOR_NAME,
         NO_NAMED_ACTORS,
-        UNABLE_TO_GET_TARGET,
-        MISSING_LEGACY_PERMISSION
+        MISSING_NAMESPACE,
+        MISSING_ACTOR_NAME,
+        MISSING_APP_INFO,
+        ACTOR_NOT_PREINSTALLED,
+        INVALID_ACTOR,
+        ALLOWED
     }
 }
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 07527c2..5b5ec42 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -778,7 +778,7 @@
         void registerApkInApex(AndroidPackage pkg) {
             synchronized (mLock) {
                 for (ActiveApexInfo aai : mActiveApexInfosCache) {
-                    if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) {
+                    if (pkg.getBaseApkPath().startsWith(aai.apexDirectory.getAbsolutePath())) {
                         List<String> apks = mApksInApex.get(aai.apexModuleName);
                         if (apks == null) {
                             apks = Lists.newArrayList();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index f168ac7..dac2e4f 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -303,7 +303,8 @@
 
         private void updateEnabledState(@NonNull AndroidPackage pkg) {
             // TODO(b/135203078): Do not use toAppInfo
-            final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
+            // TODO(b/167551701): Make changeId non-logging
+            final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternalNoLogging(
                     PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
             if (enabled) {
                 mDisabledPackages.remove(pkg.getPackageName());
@@ -639,24 +640,74 @@
         }
     }
 
-    private void updateEntireShouldFilterCacheAsync() {
-        mBackgroundExecutor.execute(this::updateEntireShouldFilterCache);
-    }
-
     private void updateEntireShouldFilterCache() {
         mStateProvider.runWithState((settings, users) -> {
             SparseArray<SparseBooleanArray> cache =
-                    new SparseArray<>(users.length * settings.size());
-            for (int i = settings.size() - 1; i >= 0; i--) {
-                updateShouldFilterCacheForPackage(cache,
-                        null /*skipPackage*/, settings.valueAt(i), settings, users, i);
-            }
+                    updateEntireShouldFilterCacheInner(settings, users);
             synchronized (mCacheLock) {
                 mShouldFilterCache = cache;
             }
         });
     }
 
+    private SparseArray<SparseBooleanArray> updateEntireShouldFilterCacheInner(
+            ArrayMap<String, PackageSetting> settings, UserInfo[] users) {
+        SparseArray<SparseBooleanArray> cache =
+                new SparseArray<>(users.length * settings.size());
+        for (int i = settings.size() - 1; i >= 0; i--) {
+            updateShouldFilterCacheForPackage(cache,
+                    null /*skipPackage*/, settings.valueAt(i), settings, users, i);
+        }
+        return cache;
+    }
+
+    private void updateEntireShouldFilterCacheAsync() {
+        mBackgroundExecutor.execute(() -> {
+            final ArrayMap<String, PackageSetting> settingsCopy = new ArrayMap<>();
+            final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>();
+            final UserInfo[][] usersRef = new UserInfo[1][];
+            mStateProvider.runWithState((settings, users) -> {
+                packagesCache.ensureCapacity(settings.size());
+                settingsCopy.putAll(settings);
+                usersRef[0] = users;
+                // store away the references to the immutable packages, since settings are retained
+                // during updates.
+                for (int i = 0, max = settings.size(); i < max; i++) {
+                    final AndroidPackage pkg = settings.valueAt(i).pkg;
+                    packagesCache.put(settings.keyAt(i), pkg);
+                }
+            });
+            SparseArray<SparseBooleanArray> cache =
+                    updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]);
+            boolean[] changed = new boolean[1];
+            // We have a cache, let's make sure the world hasn't changed out from under us.
+            mStateProvider.runWithState((settings, users) -> {
+                if (settings.size() != settingsCopy.size()) {
+                    changed[0] = true;
+                    return;
+                }
+                for (int i = 0, max = settings.size(); i < max; i++) {
+                    final AndroidPackage pkg = settings.valueAt(i).pkg;
+                    if (!Objects.equals(pkg, packagesCache.get(settings.keyAt(i)))) {
+                        changed[0] = true;
+                        return;
+                    }
+                }
+            });
+            if (changed[0]) {
+                // Something has changed, just update the cache inline with the lock held
+                updateEntireShouldFilterCache();
+                if (DEBUG_LOGGING) {
+                    Slog.i(TAG, "Rebuilding cache with lock due to package change.");
+                }
+            } else {
+                synchronized (mCacheLock) {
+                    mShouldFilterCache = cache;
+                }
+            }
+        });
+    }
+
     public void onUsersChanged() {
         synchronized (mCacheLock) {
             if (mShouldFilterCache != null) {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index d25ddad..4be509b 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -623,7 +623,6 @@
             AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
 
             if (pkg != null) {
-                // TODO(b/135203078): Print AppInfo?
                 pw.print("      applicationInfo="); pw.println(pkg.toAppInfoWithoutState());
             }
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index eddab76..7db2319 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -384,17 +384,17 @@
             if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
                 continue;
             }
-            if (pkg.getCodePath() == null) {
+            if (pkg.getPath() == null) {
                 Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath");
                 continue;
             }
 
             // If the path is in /system, /vendor, /product or /system_ext, ignore. It will
             // have been ota-dexopted into /data/ota and moved into the dalvik-cache already.
-            if (pkg.getCodePath().startsWith("/system")
-                    || pkg.getCodePath().startsWith("/vendor")
-                    || pkg.getCodePath().startsWith("/product")
-                    || pkg.getCodePath().startsWith("/system_ext")) {
+            if (pkg.getPath().startsWith("/system")
+                    || pkg.getPath().startsWith("/vendor")
+                    || pkg.getPath().startsWith("/product")
+                    || pkg.getPath().startsWith("/system_ext")) {
                 continue;
             }
 
@@ -408,7 +408,7 @@
             for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                 for (String path : paths) {
                     String oatDir = PackageDexOptimizer.getOatDir(
-                            new File(pkg.getCodePath())).getAbsolutePath();
+                            new File(pkg.getPath())).getAbsolutePath();
 
                     // TODO: Check first whether there is an artifact, to save the roundtrip time.
 
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 8af7e1f..da4ea16 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -136,7 +136,7 @@
         // Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
         // current state in PackageSetting is irrelevant.
         return deriveNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
-                appLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
+                appLib32InstallDir, pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(),
                 isUpdatedSystemApp);
     }
 
@@ -205,11 +205,11 @@
 
     @Override
     public Abis getBundledAppAbis(AndroidPackage pkg) {
-        final String apkName = deriveCodePathName(pkg.getCodePath());
+        final String apkName = deriveCodePathName(pkg.getPath());
 
         // If "/system/lib64/apkname" exists, assume that is the per-package
         // native library directory to use; otherwise use "/system/lib/apkname".
-        final String apkRoot = calculateBundledApkRoot(pkg.getBaseCodePath());
+        final String apkRoot = calculateBundledApkRoot(pkg.getBaseApkPath());
         final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName);
         return abis;
     }
@@ -223,7 +223,7 @@
      * @param apkName the name of the installed package.
      */
     private Abis getBundledAppAbi(AndroidPackage pkg, String apkRoot, String apkName) {
-        final File codeFile = new File(pkg.getCodePath());
+        final File codeFile = new File(pkg.getPath());
 
         final boolean has64BitLibs;
         final boolean has32BitLibs;
@@ -304,15 +304,15 @@
         String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
         final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
                 new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
-                PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(),
-                pkg.getBaseCodePath(), pkg.isSystem(),
+                PackageManagerService.sAppLib32InstallDir, pkg.getPath(),
+                pkg.getBaseApkPath(), pkg.isSystem(),
                 isUpdatedSystemApp);
 
         final boolean extractLibs = shouldExtractLibs(pkg, isUpdatedSystemApp);
 
         final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
         final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
-        final boolean onIncremental = isIncrementalPath(pkg.getCodePath());
+        final boolean onIncremental = isIncrementalPath(pkg.getPath());
 
         String primaryCpuAbi = null;
         String secondaryCpuAbi = null;
@@ -453,7 +453,7 @@
         final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
         return new Pair<>(abis,
                 deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
-                        pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
+                        pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(),
                         isUpdatedSystemApp));
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0d8ba3e..42e6d8f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -199,7 +199,7 @@
             throw new IllegalStateException("Inconsistent information "
                 + "between PackageParser.Package and its ApplicationInfo. "
                 + "pkg.getAllCodePaths=" + paths
-                + " pkg.getBaseCodePath=" + pkg.getBaseCodePath()
+                + " pkg.getBaseCodePath=" + pkg.getBaseApkPath()
                 + " pkg.getSplitCodePaths="
                 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
         }
@@ -772,7 +772,7 @@
         if (!AndroidPackageUtils.canHaveOatDir(pkg, isUpdatedSystemApp)) {
             return null;
         }
-        File codePath = new File(pkg.getCodePath());
+        File codePath = new File(pkg.getPath());
         if (!codePath.isDirectory()) {
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ca12532..17cd8f5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2083,7 +2083,7 @@
         if (ps == null) {
             return 0;
         }
-        final File apkDirOrPath = ps.getCodePath();
+        final File apkDirOrPath = ps.getPath();
         if (apkDirOrPath == null) {
             return 0;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89e0393..fcf5d96 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2220,7 +2220,7 @@
             // Send installed broadcasts if the package is not a static shared lib.
             if (res.pkg.getStaticSharedLibName() == null) {
                 mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(
-                        res.pkg.getBaseCodePath());
+                        res.pkg.getBaseApkPath());
 
                 // Send added for users that see the package for the first time
                 // sendPackageAddedForNewUsers also deals with system apps
@@ -3101,7 +3101,7 @@
             final int packageSettingCount = mSettings.mPackages.size();
             for (int i = packageSettingCount - 1; i >= 0; i--) {
                 PackageSetting ps = mSettings.mPackages.valueAt(i);
-                if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists())
+                if (!isExternal(ps) && (ps.getPath() == null || !ps.getPath().exists())
                         && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
                     mSettings.mPackages.removeAt(i);
                     mSettings.enableSystemPackageLPw(ps.name);
@@ -3266,11 +3266,11 @@
                             logCriticalInfo(Log.WARN,
                                     "Expecting better updated system app for " + ps.name
                                     + "; removing system app.  Last known"
-                                    + " codePath=" + ps.getCodePathString()
+                                    + " codePath=" + ps.getPathString()
                                     + ", versionCode=" + ps.versionCode
                                     + "; scanned versionCode=" + scannedPkg.getLongVersionCode());
                             removePackageLI(scannedPkg, true);
-                            mExpectingBetter.put(ps.name, ps.getCodePath());
+                            mExpectingBetter.put(ps.name, ps.getPath());
                         }
 
                         continue;
@@ -3293,14 +3293,14 @@
                         // code path, but, changes the package name.
                         final PackageSetting disabledPs =
                                 mSettings.getDisabledSystemPkgLPr(ps.name);
-                        if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists()
+                        if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
                                 || disabledPs.pkg == null) {
                             possiblyDeletedUpdatedSystemApps.add(ps.name);
                         } else {
                             // We're expecting that the system app should remain disabled, but add
                             // it to expecting better to recover in case the data version cannot
                             // be scanned.
-                            mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath());
+                            mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
                         }
                     }
                 }
@@ -3373,7 +3373,7 @@
                         // special privileges
                         removePackageLI(pkg, true);
                         try {
-                            final File codePath = new File(pkg.getCodePath());
+                            final File codePath = new File(pkg.getPath());
                             scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
                         } catch (PackageManagerException e) {
                             Slog.e(TAG, "Failed to parse updated, ex-system package: "
@@ -3854,7 +3854,7 @@
                         // If we don't, installing the system package fails during scan
                         enableSystemPackageLPw(stubPkg);
                     }
-                    installPackageFromSystemLIF(stubPkg.getCodePath(),
+                    installPackageFromSystemLIF(stubPkg.getPath(),
                             mUserManager.getUserIds() /*allUserHandles*/, null /*origUserHandles*/,
                             true /*writeSettings*/);
                 } catch (PackageManagerException pme) {
@@ -3878,7 +3878,7 @@
             clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
                     | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
             mDexManager.notifyPackageUpdated(pkg.getPackageName(),
-                    pkg.getBaseCodePath(), pkg.getSplitCodePaths());
+                    pkg.getBaseApkPath(), pkg.getSplitCodePaths());
         }
         return true;
     }
@@ -3890,10 +3890,10 @@
             Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
         }
         // uncompress the binary to its eventual destination on /data
-        final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getCodePath());
+        final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
         if (scanFile == null) {
             throw new PackageManagerException(
-                    "Unable to decompress stub at " + stubPkg.getCodePath());
+                    "Unable to decompress stub at " + stubPkg.getPath());
         }
         synchronized (mLock) {
             mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
@@ -9261,11 +9261,11 @@
         // When upgrading from pre-N MR1, verify the package time stamp using the package
         // directory and not the APK file.
         final long lastModifiedTime = mIsPreNMR1Upgrade
-                ? new File(parsedPackage.getCodePath()).lastModified()
+                ? new File(parsedPackage.getPath()).lastModified()
                 : getLastModifiedTime(parsedPackage);
         final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
         if (ps != null && !forceCollect
-                && ps.getCodePathString().equals(parsedPackage.getCodePath())
+                && ps.getPathString().equals(parsedPackage.getPath())
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
                 && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
@@ -9283,8 +9283,8 @@
             Slog.w(TAG, "PackageSetting for " + ps.name
                     + " is missing signatures.  Collecting certs again to recover them.");
         } else {
-            Slog.i(TAG, parsedPackage.getCodePath() + " changed; collecting certs" +
-                    (forceCollect ? " (forced)" : ""));
+            Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+                    + (forceCollect ? " (forced)" : ""));
         }
 
         try {
@@ -9368,7 +9368,7 @@
      * Returns if forced apk verification can be skipped for the whole package, including splits.
      */
     private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
-        if (!canSkipForcedApkVerification(pkg.getBaseCodePath())) {
+        if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
             return false;
         }
         // TODO: Allow base and splits to be verified individually.
@@ -9499,7 +9499,7 @@
         }
 
         final boolean newPkgChangedPaths = pkgAlreadyExists
-                && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath());
+                && !pkgSetting.getPathString().equals(parsedPackage.getPath());
         final boolean newPkgVersionGreater =
                 pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
         final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
@@ -9518,11 +9518,11 @@
                     "System package updated;"
                     + " name: " + pkgSetting.name
                     + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
-                    + "; " + pkgSetting.getCodePathString()
-                            + " --> " + parsedPackage.getCodePath());
+                    + "; " + pkgSetting.getPathString()
+                            + " --> " + parsedPackage.getPath());
 
             final InstallArgs args = createInstallArgsForExisting(
-                    pkgSetting.getCodePathString(), getAppDexInstructionSets(
+                    pkgSetting.getPathString(), getAppDexInstructionSets(
                             pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
             args.cleanUpResourcesLI();
             synchronized (mLock) {
@@ -9535,7 +9535,7 @@
             // equal to the version on the /data partition. Throw an exception and use
             // the application already installed on the /data partition.
             throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
-                    + " at " + parsedPackage.getCodePath() + " ignored: updated version "
+                    + " at " + parsedPackage.getPath() + " ignored: updated version "
                     + pkgSetting.versionCode + " better than this "
                     + parsedPackage.getLongVersionCode());
         }
@@ -9597,10 +9597,10 @@
                                 + " name: " + pkgSetting.name
                                 + "; " + pkgSetting.versionCode + " --> "
                                 + parsedPackage.getLongVersionCode()
-                                + "; " + pkgSetting.getCodePathString() + " --> "
-                                + parsedPackage.getCodePath());
+                                + "; " + pkgSetting.getPathString() + " --> "
+                                + parsedPackage.getPath());
                 InstallArgs args = createInstallArgsForExisting(
-                        pkgSetting.getCodePathString(), getAppDexInstructionSets(
+                        pkgSetting.getPathString(), getAppDexInstructionSets(
                                 pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -9613,10 +9613,10 @@
                 logCriticalInfo(Log.INFO,
                         "System package disabled;"
                                 + " name: " + pkgSetting.name
-                                + "; old: " + pkgSetting.getCodePathString() + " @ "
+                                + "; old: " + pkgSetting.getPathString() + " @ "
                                 + pkgSetting.versionCode
-                                + "; new: " + parsedPackage.getCodePath() + " @ "
-                                + parsedPackage.getCodePath());
+                                + "; new: " + parsedPackage.getPath() + " @ "
+                                + parsedPackage.getPath());
             }
         }
 
@@ -9797,7 +9797,7 @@
      * Return the prebuilt profile path given a package base code path.
      */
     private static String getPrebuildProfilePath(AndroidPackage pkg) {
-        return pkg.getBaseCodePath() + ".prof";
+        return pkg.getBaseApkPath() + ".prof";
     }
 
     /**
@@ -11450,7 +11450,7 @@
                         if (changedAbiCodePath == null) {
                             changedAbiCodePath = new ArrayList<>();
                         }
-                        changedAbiCodePath.add(ps.getCodePathString());
+                        changedAbiCodePath.add(ps.getPathString());
                     }
                 }
             }
@@ -11545,7 +11545,7 @@
         }
 
         // Initialize package source and resource directories
-        final File destCodeFile = new File(parsedPackage.getCodePath());
+        final File destCodeFile = new File(parsedPackage.getPath());
 
         // We keep references to the derived CPU Abis from settings in oder to reuse
         // them in the case where we're not upgrading or booting for the first time.
@@ -11883,9 +11883,9 @@
     private static void assertCodePolicy(AndroidPackage pkg)
             throws PackageManagerException {
         final boolean shouldHaveCode = pkg.isHasCode();
-        if (shouldHaveCode && !apkHasCode(pkg.getBaseCodePath())) {
+        if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                    "Package " + pkg.getBaseCodePath() + " code is missing");
+                    "Package " + pkg.getBaseApkPath() + " code is missing");
         }
 
         if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
@@ -11917,7 +11917,7 @@
             if (parsedPackage.isDirectBootAware()) {
                 parsedPackage.setAllComponentsDirectBootAware(true);
             }
-            if (compressedFileExists(parsedPackage.getCodePath())) {
+            if (compressedFileExists(parsedPackage.getPath())) {
                 parsedPackage.setStub(true);
             }
         } else {
@@ -12008,7 +12008,7 @@
             assertCodePolicy(pkg);
         }
 
-        if (pkg.getCodePath() == null) {
+        if (pkg.getPath() == null) {
             // Bail out. The resource and code paths haven't been set.
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Code and resource paths haven't been set correctly");
@@ -12035,7 +12035,7 @@
                 if (mAndroidApplication != null) {
                     Slog.w(TAG, "*************************************************");
                     Slog.w(TAG, "Core android package being redefined.  Skipping.");
-                    Slog.w(TAG, " codePath=" + pkg.getCodePath());
+                    Slog.w(TAG, " codePath=" + pkg.getPath());
                     Slog.w(TAG, "*************************************************");
                     throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
                             "Core android package being redefined.  Skipping.");
@@ -12191,14 +12191,14 @@
                     PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
                     if (known != null) {
                         if (DEBUG_PACKAGE_SCANNING) {
-                            Log.d(TAG, "Examining " + pkg.getCodePath()
-                                    + " and requiring known path " + known.getCodePathString());
+                            Log.d(TAG, "Examining " + pkg.getPath()
+                                    + " and requiring known path " + known.getPathString());
                         }
-                        if (!pkg.getCodePath().equals(known.getCodePathString())) {
+                        if (!pkg.getPath().equals(known.getPathString())) {
                             throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
                                     "Application package " + pkg.getPackageName()
-                                    + " found at " + pkg.getCodePath()
-                                    + " but expected at " + known.getCodePathString()
+                                    + " found at " + pkg.getPath()
+                                    + " but expected at " + known.getPathString()
                                     + "; ignoring.");
                         }
                     } else {
@@ -14532,13 +14532,11 @@
             Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.pkg);
         }
 
-        // A restore should be performed at this point if (a) the install
-        // succeeded, (b) the operation is not an update, and (c) the new
-        // package has not opted out of backup participation.
+        // A restore should be requested at this point if (a) the install
+        // succeeded, (b) the operation is not an update.
         final boolean update = res.removedInfo != null
                 && res.removedInfo.removedPackage != null;
-        boolean allowBackup = res.pkg != null && res.pkg.isAllowBackup();
-        boolean doRestore = !update && allowBackup;
+        boolean doRestore = !update && res.pkg != null;
 
         // Set up the post-install work request bookkeeping.  This will be used
         // and cleaned up by the post-install event handling regardless of whether
@@ -15787,7 +15785,7 @@
         abstract boolean doRename(int status, ParsedPackage parsedPackage);
         abstract int doPostInstall(int status, int uid);
 
-        /** @see PackageSettingBase#getCodePath() */
+        /** @see PackageSettingBase#getPath() */
         abstract String getCodePath();
 
         // Need installer lock especially for dex file removal.
@@ -15968,7 +15966,7 @@
                 return false;
             }
             parsedPackage.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
-                    afterCodeFile, parsedPackage.getBaseCodePath()));
+                    afterCodeFile, parsedPackage.getBaseApkPath()));
             parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
                     afterCodeFile, parsedPackage.getSplitCodePaths()));
 
@@ -16235,7 +16233,7 @@
         InstallSource installSource = installArgs.installSource;
         final String installerPackageName = installSource.installerPackageName;
 
-        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath());
+        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
         synchronized (mLock) {
 // NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
             mPermissionManager.updatePermissions(pkgName, pkg);
@@ -16874,7 +16872,7 @@
                         // which means we are replacing another update that is already
                         // installed.  We need to make sure to delete the older one's .apk.
                         res.removedInfo.args = createInstallArgsForExisting(
-                                oldPackage.getCodePath(),
+                                oldPackage.getPath(),
                                 getAppDexInstructionSets(
                                         AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
                                                 deletedPkgSetting),
@@ -16917,7 +16915,7 @@
                         if (ps1.mOldCodePaths == null) {
                             ps1.mOldCodePaths = new ArraySet<>();
                         }
-                        Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseCodePath());
+                        Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
                         if (oldPackage.getSplitCodePaths() != null) {
                             Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
                         }
@@ -17082,7 +17080,7 @@
                     // For incremental installs, we bypass the verifier prior to install. Now
                     // that we know the package is valid, send a notice to the verifier with
                     // the root hash of the base.apk.
-                    final String baseCodePath = request.installResult.pkg.getBaseCodePath();
+                    final String baseCodePath = request.installResult.pkg.getBaseApkPath();
                     final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
                     final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
                     final int verificationId = mPendingVerificationToken++;
@@ -17127,9 +17125,9 @@
             final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
             final String packageName = pkg.getPackageName();
             final boolean onIncremental = mIncrementalManager != null
-                    && isIncrementalPath(pkg.getCodePath());
+                    && isIncrementalPath(pkg.getPath());
             if (onIncremental) {
-                IncrementalStorage storage = mIncrementalManager.openStorage(pkg.getCodePath());
+                IncrementalStorage storage = mIncrementalManager.openStorage(pkg.getPath());
                 if (storage == null) {
                     throw new IllegalArgumentException(
                             "Install: null storage for incremental package " + packageName);
@@ -17143,7 +17141,7 @@
             }
             if (reconciledPkg.prepareResult.replace) {
                 mDexManager.notifyPackageUpdated(pkg.getPackageName(),
-                        pkg.getBaseCodePath(), pkg.getSplitCodePaths());
+                        pkg.getBaseApkPath(), pkg.getSplitCodePaths());
             }
 
             // Prepare the application profiles for the new code paths.
@@ -17739,12 +17737,10 @@
                 if (parsedPackage.isStaticSharedLibrary()) {
                     // Static libs have a synthetic package name containing the version
                     // and cannot be updated as an update would get a new package name,
-                    // unless this is the exact same version code which is useful for
-                    // development.
+                    // unless this is installed from adb which is useful for development.
                     AndroidPackage existingPkg = mPackages.get(parsedPackage.getPackageName());
                     if (existingPkg != null
-                            && existingPkg.getLongVersionCode()
-                            != parsedPackage.getLongVersionCode()) {
+                            && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) {
                         throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
                                 "Packages declaring "
                                         + "static-shared libs cannot be updated");
@@ -17763,7 +17759,6 @@
                     oldPackage = mPackages.get(pkgName11);
                     existingPackage = oldPackage;
                     if (DEBUG_INSTALL) {
-                        // TODO(b/135203078): PackageImpl.toString()
                         Slog.d(TAG,
                                 "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
                     }
@@ -17797,7 +17792,7 @@
                         final byte[] digestBytes;
                         try {
                             final MessageDigest digest = MessageDigest.getInstance("SHA-512");
-                            updateDigest(digest, new File(parsedPackage.getBaseCodePath()));
+                            updateDigest(digest, new File(parsedPackage.getBaseApkPath()));
                             if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
                                 for (String path : parsedPackage.getSplitCodePaths()) {
                                     updateDigest(digest, new File(path));
@@ -17977,7 +17972,7 @@
             synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
                 if (ps != null && ps.isPrivileged()) {
-                    fsverityCandidates.put(pkg.getBaseCodePath(), null);
+                    fsverityCandidates.put(pkg.getBaseApkPath(), null);
                     if (pkg.getSplitCodePaths() != null) {
                         for (String splitPath : pkg.getSplitCodePaths()) {
                             fsverityCandidates.put(splitPath, null);
@@ -17988,11 +17983,11 @@
         } else {
             // NB: These files will become only accessible if the signing key is loaded in kernel's
             // .fs-verity keyring.
-            fsverityCandidates.put(pkg.getBaseCodePath(),
-                    VerityUtils.getFsveritySignatureFilePath(pkg.getBaseCodePath()));
+            fsverityCandidates.put(pkg.getBaseApkPath(),
+                    VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
 
             final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
-                    pkg.getBaseCodePath());
+                    pkg.getBaseApkPath());
             if (new File(dmPath).exists()) {
                 fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
             }
@@ -19103,7 +19098,7 @@
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
-            installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles,
+            installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
                     outInfo == null ? null : outInfo.origUsers, writeSettings);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
@@ -19228,7 +19223,7 @@
         // Delete application code and resources only for parent packages
         if (deleteCodeAndResources && (outInfo != null)) {
             outInfo.args = createInstallArgsForExisting(
-                    ps.getCodePathString(), getAppDexInstructionSets(
+                    ps.getPathString(), getAppDexInstructionSets(
                             ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
             if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
         }
@@ -19770,7 +19765,7 @@
 
         final String[] packageNames = { packageName };
         final long[] ceDataInodes = { ps.getCeDataInode(userId) };
-        final String[] codePaths = { ps.getCodePathString() };
+        final String[] codePaths = { ps.getPathString() };
 
         try {
             mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
@@ -21633,6 +21628,8 @@
                         .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
         co.onChange(true);
 
+        mAppsFilter.onSystemReady();
+
         // Disable any carrier apps. We do this very early in boot to prevent the apps from being
         // disabled after already being started.
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(
@@ -21781,9 +21778,6 @@
         mInstallerService.restoreAndApplyStagedSessionIfNeeded();
 
         mExistingPackages = null;
-
-        // We'll do this last as it builds its cache while holding mLock via callback.
-        mAppsFilter.onSystemReady();
     }
 
     public void waitForAppDataPrepared() {
@@ -22697,11 +22691,11 @@
             synchronized (mInstallLock) {
                 final AndroidPackage pkg;
                 try {
-                    pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null);
+                    pkg = scanPackageTracedLI(ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
                     loaded.add(pkg);
 
                 } catch (PackageManagerException e) {
-                    Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage());
+                    Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
                 }
 
                 if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
@@ -22788,7 +22782,7 @@
                                 false, null)) {
                             unloaded.add(pkg);
                         } else {
-                            Slog.w(TAG, "Failed to unload " + ps.getCodePath());
+                            Slog.w(TAG, "Failed to unload " + ps.getPath());
                         }
                     }
 
@@ -22842,7 +22836,7 @@
             final int packageCount = mSettings.mPackages.size();
             for (int i = 0; i < packageCount; i++) {
                 final PackageSetting ps = mSettings.mPackages.valueAt(i);
-                codePaths.add(ps.getCodePath().getAbsolutePath());
+                codePaths.add(ps.getPath().getAbsolutePath());
             }
             return codePaths;
         }
@@ -23418,7 +23412,7 @@
 
             currentVolumeUuid = ps.volumeUuid;
 
-            final File probe = new File(pkg.getCodePath());
+            final File probe = new File(pkg.getPath());
             final File probeOat = new File(probe, "oat");
             if (!probe.isDirectory() || !probeOat.isDirectory()) {
                 throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
@@ -23440,7 +23434,7 @@
             }
 
             isCurrentLocationExternal = pkg.isExternalStorage();
-            codeFile = new File(pkg.getCodePath());
+            codeFile = new File(pkg.getPath());
             installSource = ps.installSource;
             packageAbiOverride = ps.cpuAbiOverrideString;
             appId = UserHandle.getAppId(pkg.getUid());
@@ -24871,7 +24865,7 @@
                             mApexManager.getApksInApex(apexPackages.get(i).packageName);
                     for (int j = 0, apksInApex = apkNames.size(); j < apksInApex; j++) {
                         final AndroidPackage pkg = getPackage(apkNames.get(j));
-                        cacher.cleanCachedResult(new File(pkg.getCodePath()));
+                        cacher.cleanCachedResult(new File(pkg.getPath()));
                     }
                 }
             }
@@ -24947,7 +24941,7 @@
                             Slog.e(TAG, "failed to find package " + packageName);
                             return false;
                         }
-                        overlayPaths.add(pkg.getBaseCodePath());
+                        overlayPaths.add(pkg.getBaseApkPath());
                     }
                 }
 
@@ -25663,7 +25657,7 @@
                 pkgSetting.getPkgState().isUpdatedSystemApp())) {
             return null;
         }
-        File codePath = new File(pkg.getCodePath());
+        File codePath = new File(pkg.getPath());
         if (codePath.isDirectory()) {
             return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 5553cd0..e5dad85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -326,11 +326,11 @@
     }
 
     public static long getLastModifiedTime(AndroidPackage pkg) {
-        final File srcFile = new File(pkg.getCodePath());
+        final File srcFile = new File(pkg.getPath());
         if (!srcFile.isDirectory()) {
             return srcFile.lastModified();
         }
-        final File baseFile = new File(pkg.getBaseCodePath());
+        final File baseFile = new File(pkg.getBaseApkPath());
         long maxModifiedTime = baseFile.lastModified();
         if (pkg.getSplitCodePaths() != null) {
             for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index a3a7273..4476e8a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -44,8 +44,33 @@
 public class PackageSetting extends PackageSettingBase {
     int appId;
 
+    /**
+     * This can be null whenever a physical APK on device is missing. This can be the result of
+     * removing an external storage device where the APK resides.
+     *
+     * This will result in the system reading the {@link PackageSetting} from disk, but without
+     * being able to parse the base APK's AndroidManifest.xml to read all of its metadata. The data
+     * that is written and read in {@link Settings} includes a minimal set of metadata needed to
+     * perform other checks in the system.
+     *
+     * This is important in order to enforce uniqueness within the system, as the package, even if
+     * on a removed storage device, is still considered installed. Another package of the same
+     * application ID or declaring the same permissions or similar cannot be installed.
+     *
+     * Re-attaching the storage device to make the APK available should allow the user to use the
+     * app once the device reboots or otherwise re-scans it.
+     *
+     * It is expected that all code that uses a {@link PackageSetting} understands this inner field
+     * may be null. Note that this relationship only works one way. It should not be possible to
+     * have an entry inside {@link PackageManagerService#mPackages} without a corresponding
+     * {@link PackageSetting} inside {@link Settings#mPackages}.
+     *
+     * @deprecated Use {@link #getPkg()}. The setter is favored to avoid unintended mutation.
+     */
     @Nullable
+    @Deprecated
     public AndroidPackage pkg;
+
     /**
      * WARNING. The object reference is important. We perform integer equality and NOT
      * object equality to check whether shared user settings are the same.
@@ -104,6 +129,12 @@
         doCopy(orig);
     }
 
+    /** @see #pkg **/
+    @Nullable
+    public AndroidPackage getPkg() {
+        return pkg;
+    }
+
     public int getSharedUserId() {
         if (sharedUser != null) {
             return sharedUser.userId;
@@ -200,7 +231,6 @@
         return installPermissionsFixed;
     }
 
-    // TODO(b/135203078): Remove these in favor of reading from the package directly
     public boolean isPrivileged() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 6010344..a7bbf8d 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -40,6 +40,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.File;
 import java.util.Arrays;
@@ -59,13 +60,9 @@
     public final String name;
     final String realName;
 
-    /**
-     * Path where this package was found on disk. For monolithic packages
-     * this is path to single base APK file; for cluster packages this is
-     * path to the cluster directory.
-     */
-    private File mCodePath;
-    private String mCodePathString;
+    /** @see AndroidPackage#getPath() */
+    private File mPath;
+    private String mPathString;
 
     String[] usesStaticLibraries;
     long[] usesStaticLibrariesVersions;
@@ -136,7 +133,7 @@
 
     boolean forceQueryableOverride;
 
-    PackageSettingBase(String name, String realName, @NonNull File codePath,
+    PackageSettingBase(String name, String realName, @NonNull File path,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int pkgPrivateFlags,
@@ -146,7 +143,7 @@
         this.realName = realName;
         this.usesStaticLibraries = usesStaticLibraries;
         this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
-        setCodePath(codePath);
+        setPath(path);
         this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
         this.primaryCpuAbiString = primaryCpuAbiString;
         this.secondaryCpuAbiString = secondaryCpuAbiString;
@@ -230,7 +227,7 @@
     }
 
     private void doCopy(PackageSettingBase orig) {
-        setCodePath(orig.getCodePath());
+        setPath(orig.getPath());
         cpuAbiOverrideString = orig.cpuAbiOverrideString;
         firstInstallTime = orig.firstInstallTime;
         installPermissionsFixed = orig.installPermissionsFixed;
@@ -697,18 +694,23 @@
         return userState.harmfulAppWarning;
     }
 
-    PackageSettingBase setCodePath(@NonNull File codePath) {
-        this.mCodePath = codePath;
-        this.mCodePathString = codePath.toString();
+    /**
+     * @see #mPath
+     */
+    PackageSettingBase setPath(@NonNull File path) {
+        this.mPath = path;
+        this.mPathString = path.toString();
         return this;
     }
 
-    File getCodePath() {
-        return mCodePath;
+    /** @see #mPath */
+    File getPath() {
+        return mPath;
     }
 
-    String getCodePathString() {
-        return mCodePathString;
+    /** @see #mPath */
+    String getPathString() {
+        return mPathString;
     }
 
     /**
@@ -733,7 +735,7 @@
 
     protected PackageSettingBase updateFrom(PackageSettingBase other) {
         super.copyFrom(other);
-        setCodePath(other.getCodePath());
+        setPath(other.getPath());
         this.usesStaticLibraries = other.usesStaticLibraries;
         this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
         this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bd8d0c8..74bc49d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -306,8 +306,8 @@
     private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
 
     // List of replaced system applications
-    private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
-        new ArrayMap<String, PackageSetting>();
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<>();
 
     /** List of packages that are blocked for uninstall for specific users */
     private final SparseArray<ArraySet<String>> mBlockUninstallPackages = new SparseArray<>();
@@ -538,7 +538,7 @@
             return null;
         }
         p.getPkgState().setUpdatedSystemApp(false);
-        PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(),
+        PackageSetting ret = addPackageLPw(name, p.realName, p.getPath(),
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
@@ -645,7 +645,7 @@
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
                     + pkgName + " is adopting original package " + originalPkg.name);
             pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
-            pkgSetting.setCodePath(codePath);
+            pkgSetting.setPath(codePath);
             pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
             pkgSetting.pkgFlags = pkgFlags;
             pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
@@ -770,12 +770,12 @@
                     "Updating application package " + pkgName + " failed");
         }
 
-        if (!pkgSetting.getCodePath().equals(codePath)) {
+        if (!pkgSetting.getPath().equals(codePath)) {
             final boolean isSystem = pkgSetting.isSystem();
             Slog.i(PackageManagerService.TAG,
                     "Update" + (isSystem ? " system" : "")
                     + " package " + pkgName
-                    + " code path from " + pkgSetting.getCodePathString()
+                    + " code path from " + pkgSetting.getPathString()
                     + " to " + codePath.toString()
                     + "; Retain data and using new");
             if (!isSystem) {
@@ -797,7 +797,7 @@
                 // internal to external storage or vice versa.
                 pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
             }
-            pkgSetting.setCodePath(codePath);
+            pkgSetting.setPath(codePath);
         }
         // If what we are scanning is a system (and possibly privileged) package,
         // then make it so, regardless of whether it was previously installed only
@@ -2174,32 +2174,24 @@
 
     void readUsesStaticLibLPw(XmlPullParser parser, PackageSetting outPs)
             throws IOException, XmlPullParserException {
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-            String libName = parser.getAttributeValue(null, ATTR_NAME);
-            String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION);
+        String libName = parser.getAttributeValue(null, ATTR_NAME);
+        String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION);
 
-            long libVersion = -1;
-            try {
-                libVersion = Long.parseLong(libVersionStr);
-            } catch (NumberFormatException e) {
-                // ignore
-            }
-
-            if (libName != null && libVersion >= 0) {
-                outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
-                        outPs.usesStaticLibraries, libName);
-                outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong(
-                        outPs.usesStaticLibrariesVersions, libVersion);
-            }
-
-            XmlUtils.skipCurrentTag(parser);
+        long libVersion = -1;
+        try {
+            libVersion = Long.parseLong(libVersionStr);
+        } catch (NumberFormatException e) {
+            // ignore
         }
+
+        if (libName != null && libVersion >= 0) {
+            outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
+                    outPs.usesStaticLibraries, libName);
+            outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong(
+                    outPs.usesStaticLibrariesVersions, libVersion);
+        }
+
+        XmlUtils.skipCurrentTag(parser);
     }
 
     void writeUsesStaticLibLPw(XmlSerializer serializer, String[] usesStaticLibraries,
@@ -2710,7 +2702,7 @@
         if (pkg.realName != null) {
             serializer.attribute(null, "realName", pkg.realName);
         }
-        serializer.attribute(null, "codePath", pkg.getCodePathString());
+        serializer.attribute(null, "codePath", pkg.getPathString());
         serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
         serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
         serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
@@ -2752,7 +2744,7 @@
         if (pkg.realName != null) {
             serializer.attribute(null, "realName", pkg.realName);
         }
-        serializer.attribute(null, "codePath", pkg.getCodePathString());
+        serializer.attribute(null, "codePath", pkg.getPathString());
 
         if (pkg.legacyNativeLibraryPathString != null) {
             serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
@@ -3875,6 +3867,8 @@
                     readDomainVerificationLPw(parser, packageSetting);
                 } else if (tagName.equals(TAG_MIME_GROUP)) {
                     packageSetting.mimeGroups = readMimeGroupLPw(parser, packageSetting.mimeGroups);
+                } else if (tagName.equals(TAG_USES_STATIC_LIB)) {
+                    readUsesStaticLibLPw(parser, packageSetting);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <package>: " + parser.getName());
@@ -4561,9 +4555,9 @@
             pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.sharedUser);
         }
         pw.print(prefix); pw.print("  pkg="); pw.println(pkg);
-        pw.print(prefix); pw.print("  codePath="); pw.println(ps.getCodePathString());
+        pw.print(prefix); pw.print("  codePath="); pw.println(ps.getPathString());
         if (permissionNames == null) {
-            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.getCodePathString());
+            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.getPathString());
             pw.print(prefix); pw.print("  legacyNativeLibraryDir=");
             pw.println(ps.legacyNativeLibraryPathString);
             pw.print(prefix); pw.print("  extractNativeLibs=");
@@ -4581,10 +4575,10 @@
         pw.println();
         if (pkg != null) {
             pw.print(prefix); pw.print("  versionName="); pw.println(pkg.getVersionName());
+            pw.print(prefix); pw.print("  usesNonSdkApi="); pw.println(pkg.isUsesNonSdkApi());
             pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, pkg); pw.println();
             final int apkSigningVersion = pkg.getSigningDetails().signatureSchemeVersion;
             pw.print(prefix); pw.print("  apkSigningVersion="); pw.println(apkSigningVersion);
-            // TODO(b/135203078): Is there anything to print here with AppInfo removed?
             pw.print(prefix); pw.print("  applicationInfo=");
             pw.println(pkg.toAppInfoToString());
             pw.print(prefix); pw.print("  flags=");
@@ -5180,7 +5174,6 @@
     }
 
     void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps) {
-        // TODO(b/135203078): ParsedComponent toString methods for dumping
         dumpComponents(pw, prefix, "activities:", ps.pkg.getActivities());
         dumpComponents(pw, prefix, "services:", ps.pkg.getServices());
         dumpComponents(pw, prefix, "receivers:", ps.pkg.getReceivers());
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 8000c63..587cb825 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -486,7 +486,7 @@
     public boolean compileLayouts(AndroidPackage pkg) {
         try {
             final String packageName = pkg.getPackageName();
-            final String apkPath = pkg.getBaseCodePath();
+            final String apkPath = pkg.getBaseApkPath();
             // TODO(b/143971007): Use a cross-user directory
             File dataDir = PackageInfoWithoutStateUtils.getDataDir(pkg, UserHandle.myUserId());
             final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
@@ -524,7 +524,7 @@
     private ArrayMap<String, String> getPackageProfileNames(AndroidPackage pkg) {
         ArrayMap<String, String> result = new ArrayMap<>();
         if (pkg.isHasCode()) {
-            result.put(pkg.getBaseCodePath(), ArtManager.getProfileName(null));
+            result.put(pkg.getBaseApkPath(), ArtManager.getProfileName(null));
         }
 
         String[] splitCodePaths = pkg.getSplitCodePaths();
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 6807388..fa01836 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -18,12 +18,12 @@
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.SharedLibraryInfo;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.os.ClassLoaderFactory;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.File;
 import java.util.List;
@@ -90,7 +90,7 @@
 
         // The splits have an implicit dependency on the base apk.
         // This means that we have to add the base apk file in addition to the shared libraries.
-        String baseApkName = new File(pkg.getBaseCodePath()).getName();
+        String baseApkName = new File(pkg.getBaseApkPath()).getName();
         String baseClassPath = baseApkName;
 
         // The result is stored in classLoaderContexts.
@@ -401,7 +401,7 @@
      * Assumes that the application declares a non-null array of splits.
      */
     private static String[] getSplitRelativeCodePaths(AndroidPackage pkg) {
-        String baseCodePath = new File(pkg.getBaseCodePath()).getParent();
+        String baseCodePath = new File(pkg.getBaseApkPath()).getParent();
         String[] splitCodePaths = pkg.getSplitCodePaths();
         String[] splitRelativeCodePaths = new String[ArrayUtils.size(splitCodePaths)];
         for (int i = 0; i < splitRelativeCodePaths.length; i++) {
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index 5506a52..a567266 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -40,7 +40,7 @@
     public boolean compileLayouts(AndroidPackage pkg) {
         try {
             final String packageName = pkg.getPackageName();
-            final String apkPath = pkg.getBaseCodePath();
+            final String apkPath = pkg.getBaseApkPath();
             // TODO(b/143971007): Use a cross-user directory
             File dataDir = PackageInfoWithoutStateUtils.getDataDir(pkg, UserHandle.myUserId());
             final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 99c6dd1..74ec161 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -79,7 +79,6 @@
         final PackageParserCacheHelper.ReadHelper helper = new PackageParserCacheHelper.ReadHelper(p);
         helper.startAndInstall();
 
-        // TODO(b/135203078): Hide PackageImpl constructor?
         ParsedPackage pkg = new PackageImpl(p);
 
         p.recycle();
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 09b4f89..d695a01 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -35,6 +35,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -73,7 +74,7 @@
  * @hide
  **/
 public class PackageInfoUtils {
-    private static final String TAG = PackageParser2.TAG;
+    private static final String TAG = ParsingUtils.TAG;
 
     /**
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
@@ -207,7 +208,6 @@
     public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
             @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
             @Nullable PackageSetting pkgSetting) {
-        // TODO(b/135203078): Consider cases where we don't have a PkgSetting
         if (pkg == null) {
             return null;
         }
@@ -354,7 +354,6 @@
             return null;
         }
 
-        // TODO(b/135203078): Add setting related state
         info.primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
         info.secondaryCpuAbi = AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting);
         info.nativeLibraryDir = pkg.getNativeLibraryDir();
@@ -454,7 +453,6 @@
 
     /** @see ApplicationInfo#flags */
     public static int appInfoFlags(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
-        // TODO(b/135203078): Add setting related state
         // @formatter:off
         int pkgWithoutStateFlags = PackageInfoWithoutStateUtils.appInfoFlags(pkg)
                 | flag(pkg.isSystem(), ApplicationInfo.FLAG_SYSTEM)
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 1145057..851ddd1 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -71,7 +71,7 @@
                     return platformCompat.isChangeEnabled(changeId, appInfo);
                 } catch (Exception e) {
                     // This shouldn't happen, but assume enforcement if it does
-                    Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e);
+                    Slog.wtf(TAG, "IPlatformCompat query failed", e);
                     return true;
                 }
             }
@@ -87,7 +87,7 @@
         });
     }
 
-    static final String TAG = "PackageParser2";
+    private static final String TAG = ParsingUtils.TAG;
 
     private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
     private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 39784cf..a13680a 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -23,7 +23,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
-import android.content.pm.SharedLibraryInfo;
 import android.content.pm.parsing.ParsingPackageRead;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -65,18 +64,16 @@
 
     /** Path of base APK */
     @NonNull
-    String getBaseCodePath();
+    String getBaseApkPath();
 
     /** Revision code of base APK */
     int getBaseRevisionCode();
 
     /**
-     * Path where this package was found on disk. For monolithic packages
-     * this is path to single base APK file; for cluster packages this is
-     * path to the cluster directory.
+     * The path to the folder containing the base APK and any installed splits.
      */
     @NonNull
-    String getCodePath();
+    String getPath();
 
     /**
      * Permissions requested but not in the manifest. These may have been split or migrated from
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index a6f02e7..0a56e13 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -58,7 +58,7 @@
         PackageImpl pkg = (PackageImpl) aPkg;
         ArrayList<String> paths = new ArrayList<>();
         if (pkg.isHasCode()) {
-            paths.add(pkg.getBaseCodePath());
+            paths.add(pkg.getBaseApkPath());
         }
         String[] splitCodePaths = pkg.getSplitCodePaths();
         if (!ArrayUtils.isEmpty(splitCodePaths)) {
@@ -77,7 +77,7 @@
     public static List<String> getAllCodePaths(AndroidPackage aPkg) {
         PackageImpl pkg = (PackageImpl) aPkg;
         ArrayList<String> paths = new ArrayList<>();
-        paths.add(pkg.getBaseCodePath());
+        paths.add(pkg.getBaseApkPath());
 
         String[] splitCodePaths = pkg.getSplitCodePaths();
         if (!ArrayUtils.isEmpty(splitCodePaths)) {
@@ -147,7 +147,7 @@
         if (pkg.isSystem() && !isUpdatedSystemApp) {
             return false;
         }
-        if (IncrementalManager.isIncrementalPath(pkg.getCodePath())) {
+        if (IncrementalManager.isIncrementalPath(pkg.getPath())) {
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 43365fa..0e3e110 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -136,9 +136,9 @@
     private int uid = -1;
 
     @VisibleForTesting
-    public PackageImpl(@NonNull String packageName, @NonNull String baseCodePath,
-            @NonNull String codePath, @Nullable TypedArray manifestArray, boolean isCoreApp) {
-        super(packageName, baseCodePath, codePath, manifestArray);
+    public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
+            @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) {
+        super(packageName, baseApkPath, path, manifestArray);
         this.manifestPackageName = this.packageName;
         this.coreApp = isCoreApp;
     }
@@ -247,7 +247,7 @@
 
     @Override
     public PackageImpl setCodePath(@NonNull String value) {
-        this.codePath = value;
+        this.mPath = value;
         return this;
     }
 
@@ -322,7 +322,7 @@
 
     @Override
     public PackageImpl setBaseCodePath(@NonNull String baseCodePath) {
-        this.baseCodePath = TextUtils.safeIntern(baseCodePath);
+        this.mBaseApkPath = TextUtils.safeIntern(baseCodePath);
         return this;
     }
 
@@ -430,7 +430,7 @@
     @Deprecated
     @Override
     public String toAppInfoToString() {
-        return "ApplicationInfo{"
+        return "PackageImpl{"
                 + Integer.toHexString(System.identityHashCode(this))
                 + " " + getPackageName() + "}";
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index ffdcc22..1cfc5b1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3526,7 +3526,7 @@
                                 deniedPermissions == null || !deniedPermissions.contains(perm);
                         if (permissionViolation) {
                             Slog.w(TAG, "Privileged permission " + perm + " for package "
-                                    + pkg.getPackageName() + " (" + pkg.getCodePath()
+                                    + pkg.getPackageName() + " (" + pkg.getPath()
                                     + ") not in privapp-permissions whitelist");
 
                             if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
@@ -3534,7 +3534,7 @@
                                     mPrivappPermissionsViolations = new ArraySet<>();
                                 }
                                 mPrivappPermissionsViolations.add(
-                                        pkg.getPackageName() + " (" + pkg.getCodePath() + "): "
+                                        pkg.getPackageName() + " (" + pkg.getPath() + "): "
                                                 + perm);
                             }
                         } else {
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 8af76a1..c769a50 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -20,6 +20,7 @@
 import android.app.SearchManager;
 import android.app.SearchableInfo;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -397,8 +398,9 @@
     }
 
     private String getGlobalSearchProviderSetting() {
-        return Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY);
+        final ContentResolver cr = mContext.getContentResolver();
+        return Settings.Secure.getStringForUser(cr,
+                Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY, cr.getUserId());
     }
 
     /**
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5aedfc1..2a74b3d 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -28,6 +28,8 @@
 import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
 import android.app.slice.ISliceManager;
 import android.app.slice.SliceSpec;
 import android.app.usage.UsageStatsManagerInternal;
@@ -39,7 +41,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
@@ -61,6 +65,7 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -72,7 +77,10 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
 
 public class SliceManagerService extends ISliceManager.Stub {
 
@@ -80,13 +88,16 @@
     private final Object mLock = new Object();
 
     private final Context mContext;
+    private final PackageManagerInternal mPackageManagerInternal;
     private final AppOpsManager mAppOps;
     private final AssistUtils mAssistUtils;
 
     @GuardedBy("mLock")
     private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
     @GuardedBy("mLock")
-    private final SparseArray<String> mLastAssistantPackage = new SparseArray<>();
+    private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>();
     private final Handler mHandler;
 
     private final SlicePermissionManager mPermissions;
@@ -99,6 +110,8 @@
     @VisibleForTesting
     SliceManagerService(Context context, Looper looper) {
         mContext = context;
+        mPackageManagerInternal = Objects.requireNonNull(
+                LocalServices.getService(PackageManagerInternal.class));
         mAppOps = context.getSystemService(AppOpsManager.class);
         mAssistUtils = new AssistUtils(context);
         mHandler = new Handler(looper);
@@ -111,6 +124,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
+        mRoleObserver = new RoleObserver();
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
     }
 
@@ -160,7 +174,8 @@
         mHandler.post(() -> {
             if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
                 mAppUsageStats.reportEvent(slicePkg, user,
-                        isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED);
+                        isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
+                                ? SLICE_PINNED_PRIV : SLICE_PINNED);
             }
         });
     }
@@ -425,19 +440,38 @@
     private boolean hasFullSliceAccess(String pkg, int userId) {
         long ident = Binder.clearCallingIdentity();
         try {
-            return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId);
+            boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+                    || isGrantedFullAccess(pkg, userId);
+            return ret;
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
     private boolean isAssistant(String pkg, int userId) {
-        if (pkg == null) return false;
-        if (!pkg.equals(mLastAssistantPackage.get(userId))) {
-            // Failed on cached value, try updating.
-            mLastAssistantPackage.put(userId, getAssistant(userId));
+        return getAssistantMatcher(userId).matches(pkg);
+    }
+
+    private boolean isDefaultHomeApp(String pkg, int userId) {
+        return getHomeMatcher(userId).matches(pkg);
+    }
+
+    private PackageMatchingCache getAssistantMatcher(int userId) {
+        PackageMatchingCache matcher = mAssistantLookup.get(userId);
+        if (matcher == null) {
+            matcher = new PackageMatchingCache(() -> getAssistant(userId));
+            mAssistantLookup.put(userId, matcher);
         }
-        return pkg.equals(mLastAssistantPackage.get(userId));
+        return matcher;
+    }
+
+    private PackageMatchingCache getHomeMatcher(int userId) {
+        PackageMatchingCache matcher = mHomeLookup.get(userId);
+        if (matcher == null) {
+            matcher = new PackageMatchingCache(() -> getDefaultHome(userId));
+            mHomeLookup.put(userId, matcher);
+        }
+        return matcher;
     }
 
     private String getAssistant(int userId) {
@@ -448,6 +482,111 @@
         return cn.getPackageName();
     }
 
+    /**
+     * A cached value of the default home app
+     */
+    private String mCachedDefaultHome = null;
+
+    // Based on getDefaultHome in ShortcutService.
+    // TODO: Unify if possible
+    @VisibleForTesting
+    protected String getDefaultHome(int userId) {
+
+        // Set VERIFY to true to run the cache in "shadow" mode for cache
+        // testing.  Do not commit set to true;
+        final boolean VERIFY = false;
+
+        if (mCachedDefaultHome != null) {
+            if (!VERIFY) {
+                return mCachedDefaultHome;
+            }
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+
+            // Default launcher from package manager.
+            final ComponentName defaultLauncher = mPackageManagerInternal
+                    .getHomeActivitiesAsUser(allHomeCandidates, userId);
+
+            ComponentName detected = defaultLauncher;
+
+            // Cache the default launcher.  It is not a problem if the
+            // launcher is null - eventually, the default launcher will be
+            // set to something non-null.
+            mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null);
+
+            if (detected == null) {
+                // If we reach here, that means it's the first check since the user was created,
+                // and there's already multiple launchers and there's no default set.
+                // Find the system one with the highest priority.
+                // (We need to check the priority too because of FallbackHome in Settings.)
+                // If there's no system launcher yet, then no one can access slices, until
+                // the user explicitly sets one.
+                final int size = allHomeCandidates.size();
+
+                int lastPriority = Integer.MIN_VALUE;
+                for (int i = 0; i < size; i++) {
+                    final ResolveInfo ri = allHomeCandidates.get(i);
+                    if (!ri.activityInfo.applicationInfo.isSystemApp()) {
+                        continue;
+                    }
+                    if (ri.priority < lastPriority) {
+                        continue;
+                    }
+                    detected = ri.activityInfo.getComponentName();
+                    lastPriority = ri.priority;
+                }
+            }
+            final String ret = ((detected != null) ? detected.getPackageName() : null);
+            if (VERIFY) {
+                if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) {
+                    Slog.e(TAG, "getDefaultHome() cache failure, is " +
+                           mCachedDefaultHome + " should be " + ret);
+                }
+            }
+            return ret;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void invalidateCachedDefaultHome() {
+        mCachedDefaultHome = null;
+    }
+
+    /**
+     * Listen for changes in the roles, and invalidate the cached default
+     * home as necessary.
+     */
+    private RoleObserver mRoleObserver;
+
+    class RoleObserver implements OnRoleHoldersChangedListener {
+        private RoleManager mRm;
+        private final Executor mExecutor;
+
+        RoleObserver() {
+            mExecutor = mContext.getMainExecutor();
+            register();
+        }
+
+        public void register() {
+            mRm = mContext.getSystemService(RoleManager.class);
+            if (mRm != null) {
+                mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL);
+                invalidateCachedDefaultHome();
+            }
+        }
+
+        @Override
+        public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
+            if (RoleManager.ROLE_HOME.equals(roleName)) {
+                invalidateCachedDefaultHome();
+            }
+        }
+    }
+
     private boolean isGrantedFullAccess(String pkg, int userId) {
         return mPermissions.hasFullAccess(pkg, userId);
     }
@@ -496,6 +635,30 @@
         return mPermissions.getAllPackagesGranted(pkg);
     }
 
+    /**
+     * Holder that caches a package that has access to a slice.
+     */
+    static class PackageMatchingCache {
+
+        private final Supplier<String> mPkgSource;
+        private String mCurrentPkg;
+
+        public PackageMatchingCache(Supplier<String> pkgSource) {
+            mPkgSource = pkgSource;
+        }
+
+        public boolean matches(String pkgCandidate) {
+            if (pkgCandidate == null) return false;
+
+            if (Objects.equals(pkgCandidate, mCurrentPkg)) {
+                return true;
+            }
+            // Failed on cached value, try updating.
+            mCurrentPkg = mPkgSource.get();
+            return Objects.equals(pkgCandidate, mCurrentPkg);
+        }
+    }
+
     public static class Lifecycle extends SystemService {
         private SliceManagerService mService;
 
diff --git a/services/core/java/com/android/server/telecom/InternalServiceRepository.java b/services/core/java/com/android/server/telecom/InternalServiceRepository.java
new file mode 100644
index 0000000..76ea5c7
--- /dev/null
+++ b/services/core/java/com/android/server/telecom/InternalServiceRepository.java
@@ -0,0 +1,63 @@
+/*
+ * 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.server.telecom;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Process;
+
+import com.android.internal.telecom.IDeviceIdleControllerAdapter;
+import com.android.internal.telecom.IInternalServiceRetriever;
+import com.android.server.DeviceIdleInternal;
+
+/**
+ * The Telecom APK can not access services stored in LocalService directly and since it is in the
+ * SYSTEM process, it also can not use the *Manager interfaces
+ * (see {@link Context#enforceCallingPermission(String, String)}). Instead, we must wrap these local
+ * services in binder interfaces to allow Telecom access.
+ */
+public class InternalServiceRepository extends IInternalServiceRetriever.Stub {
+
+    private final IDeviceIdleControllerAdapter.Stub mDeviceIdleControllerAdapter =
+            new IDeviceIdleControllerAdapter.Stub() {
+        @Override
+        public void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle,
+                String reason) {
+            mDeviceIdleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
+                    duration, userHandle, true /*sync*/, reason);
+        }
+    };
+
+    private final DeviceIdleInternal mDeviceIdleController;
+
+    public InternalServiceRepository(DeviceIdleInternal deviceIdleController) {
+        mDeviceIdleController = deviceIdleController;
+    }
+
+    @Override
+    public IDeviceIdleControllerAdapter getDeviceIdleController() {
+        ensureSystemProcess();
+        return mDeviceIdleControllerAdapter;
+    }
+
+    private void ensureSystemProcess() {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            // Correctness check - this should never happen.
+            throw new SecurityException("SYSTEM ONLY API.");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index a853529..52ad893 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -35,7 +35,10 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telecom.ITelecomLoader;
+import com.android.internal.telecom.ITelecomService;
 import com.android.internal.telephony.SmsApplication;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerService;
@@ -53,16 +56,13 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             // Normally, we would listen for death here, but since telecom runs in the same process
-            // as this loader (process="system") thats redundant here.
+            // as this loader (process="system") that's redundant here.
             try {
-                service.linkToDeath(new IBinder.DeathRecipient() {
-                    @Override
-                    public void binderDied() {
-                        connectToTelecom();
-                    }
-                }, 0);
+                ITelecomLoader telecomLoader = ITelecomLoader.Stub.asInterface(service);
+                ITelecomService telecomService = telecomLoader.createTelecomService(mServiceRepo);
+
                 SmsApplication.getDefaultMmsApplication(mContext, false);
-                ServiceManager.addService(Context.TELECOM_SERVICE, service);
+                ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder());
 
                 synchronized (mLock) {
                     final PermissionManagerServiceInternal permissionManager =
@@ -114,6 +114,8 @@
     @GuardedBy("mLock")
     private TelecomServiceConnection mServiceConnection;
 
+    private InternalServiceRepository mServiceRepo;
+
     public TelecomLoaderService(Context context) {
         super(context);
         mContext = context;
@@ -129,6 +131,8 @@
         if (phase == PHASE_ACTIVITY_MANAGER_READY) {
             registerDefaultAppNotifier();
             registerCarrierConfigChangedReceiver();
+            // core services will have already been loaded.
+            setupServiceRepository();
             connectToTelecom();
         }
     }
@@ -154,6 +158,11 @@
         }
     }
 
+    private void setupServiceRepository() {
+        DeviceIdleInternal deviceIdleInternal = getLocalService(DeviceIdleInternal.class);
+        mServiceRepo = new InternalServiceRepository(deviceIdleInternal);
+    }
+
 
     private void registerDefaultAppProviders() {
         final PermissionManagerServiceInternal permissionManager =
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6df46ed..ad28124 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4628,18 +4628,26 @@
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(this);
     }
 
-    /** @return {@code true} if this activity should be made visible. */
-    boolean shouldBeVisible(boolean behindFullscreenActivity, boolean ignoringKeyguard) {
+    private void updateVisibleIgnoringKeyguard(boolean behindFullscreenActivity) {
         // Check whether activity should be visible without Keyguard influence
         visibleIgnoringKeyguard = (!behindFullscreenActivity || mLaunchTaskBehind)
                 && okToShowLocked();
+    }
+
+    /** @return {@code true} if this activity should be made visible. */
+    private boolean shouldBeVisible(boolean behindFullscreenActivity, boolean ignoringKeyguard) {
+        updateVisibleIgnoringKeyguard(behindFullscreenActivity);
 
         if (ignoringKeyguard) {
             return visibleIgnoringKeyguard;
         }
 
+        return shouldBeVisibleUnchecked();
+    }
+
+    boolean shouldBeVisibleUnchecked() {
         final Task stack = getRootTask();
-        if (stack == null) {
+        if (stack == null || !visibleIgnoringKeyguard) {
             return false;
         }
 
@@ -4652,26 +4660,30 @@
             return false;
         }
 
-        // Check if the activity is on a sleeping display, and if it can turn it ON.
-        if (mDisplayContent.isSleeping()) {
-            final boolean canTurnScreenOn = !mSetToSleep || canTurnScreenOn()
-                    || canShowWhenLocked() || containsDismissKeyguardWindow();
-            if (!canTurnScreenOn) {
-                return false;
-            }
+        // Check if the activity is on a sleeping display
+        // TODO b/163993448 mSetToSleep is required when restarting an existing activity, try to
+        // remove it if possible.
+        if (mSetToSleep && mDisplayContent.isSleeping()) {
+            return false;
         }
 
+        return mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
+    }
+
+    void updateVisibility(boolean behindFullscreenActivity) {
+        updateVisibleIgnoringKeyguard(behindFullscreenActivity);
+        final Task task = getRootTask();
+        if (task == null || !visibleIgnoringKeyguard) {
+            return;
+        }
         // Now check whether it's really visible depending on Keyguard state, and update
         // {@link ActivityStack} internal states.
         // Inform the method if this activity is the top activity of this stack, but exclude the
         // case where this is the top activity in a pinned stack.
-        final boolean isTop = this == stack.getTopNonFinishingActivity();
-        final boolean isTopNotPinnedStack = stack.isAttached()
-                && stack.getDisplayArea().isTopNotFinishNotPinnedStack(stack);
-        final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this,
-                visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
-
-        return visibleIgnoringDisplayStatus;
+        final boolean isTop = this == task.getTopNonFinishingActivity();
+        final boolean isTopNotPinnedStack = task.isAttached()
+                && task.getDisplayArea().isTopNotFinishNotPinnedStack(task);
+        task.updateKeyguardVisibility(this, isTop && isTopNotPinnedStack);
     }
 
     boolean shouldBeVisible() {
@@ -6643,8 +6655,7 @@
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
         final int requestedOrientation = getRequestedConfigurationOrientation();
-        final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED
-                && !mDisplayContent.ignoreRotationForApps();
+        final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
         final int orientation = orientationRequested
                 ? requestedOrientation
                 : newParentConfiguration.orientation;
@@ -7538,10 +7549,8 @@
             return false;
         }
         final Task stack = getRootTask();
-        return stack != null
-                && !stack.inMultiWindowMode()
-                && stack.checkKeyguardVisibility(this, true /* shouldBeVisible */,
-                        stack.topRunningActivity() == this /* isTop */);
+        return stack != null && !stack.inMultiWindowMode()
+                && mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
     }
 
     void setTurnScreenOn(boolean turnScreenOn) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 9df192b7..5196416 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -759,8 +759,7 @@
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
 
-            if (r.getRootTask().checkKeyguardVisibility(r, true /* shouldBeVisible */,
-                    true /* isTop */) && r.allowMoveToFront()) {
+            if (mKeyguardController.checkKeyguardVisibility(r) && r.allowMoveToFront()) {
                 // We only set the visibility to true if the activity is not being launched in
                 // background, and is allowed to be visible based on keyguard state. This avoids
                 // setting this into motion in window manager that is later cancelled due to later
@@ -2298,11 +2297,15 @@
     }
 
     /** Ends a batch of visibility updates. */
-    void endActivityVisibilityUpdate() {
-        mVisibilityTransactionDepth--;
-        if (mVisibilityTransactionDepth == 0) {
+    void endActivityVisibilityUpdate(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        if (mVisibilityTransactionDepth == 1) {
             getKeyguardController().visibilitiesUpdated();
+            // commit visibility to activities
+            mRootWindowContainer.commitActivitiesVisible(starting, configChanges, preserveWindows,
+                    notifyClients);
         }
+        mVisibilityTransactionDepth--;
     }
 
     /** Returns {@code true} if the caller is on the path to update visibility. */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 080a438..2657eb2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -137,6 +137,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.ActivityThread;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
@@ -2158,14 +2159,14 @@
     }
 
     @Override
-    public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+    public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getFocusedRootTaskInfo()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 Task focusedStack = getTopDisplayFocusedStack();
                 if (focusedStack != null) {
-                    return mRootWindowContainer.getStackInfo(focusedStack.mTaskId);
+                    return mRootWindowContainer.getRootTaskInfo(focusedStack.mTaskId);
                 }
                 return null;
             }
@@ -2893,14 +2894,13 @@
         }
     }
 
-    // TODO(148895075): deprecate and replace with task equivalents
     @Override
-    public List<ActivityManager.StackInfo> getAllStackInfos() {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+    public List<RootTaskInfo> getAllRootTaskInfos() {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllRootTaskInfos()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mRootWindowContainer.getAllStackInfos(INVALID_DISPLAY);
+                return mRootWindowContainer.getAllRootTaskInfos(INVALID_DISPLAY);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2908,26 +2908,12 @@
     }
 
     @Override
-    public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+    public RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getRootTaskInfo()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mRootWindowContainer.getStackInfo(windowingMode, activityType);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    // TODO(148895075): deprecate and replace with task equivalents
-    @Override
-    public List<ActivityManager.StackInfo> getAllStackInfosOnDisplay(int displayId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                return mRootWindowContainer.getAllStackInfos(displayId);
+                return mRootWindowContainer.getRootTaskInfo(windowingMode, activityType);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2935,13 +2921,27 @@
     }
 
     @Override
-    public ActivityManager.StackInfo getStackInfoOnDisplay(int windowingMode, int activityType,
+    public List<RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "getAllRootTaskInfosOnDisplay()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mRootWindowContainer.getAllRootTaskInfos(displayId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public RootTaskInfo getRootTaskInfoOnDisplay(int windowingMode, int activityType,
             int displayId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getRootTaskInfoOnDisplay()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mRootWindowContainer.getStackInfo(windowingMode, activityType, displayId);
+                return mRootWindowContainer.getRootTaskInfo(windowingMode, activityType, displayId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8ccbd11..4db121b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -28,7 +28,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -36,20 +35,21 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.GONE;
-import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -367,13 +367,6 @@
     final float mCloseToSquareMaxAspectRatio;
 
     /**
-     * If this is true, we would not rotate the display for apps. The rotation would be either the
-     * sensor rotation or the user rotation, controlled by
-     * {@link WindowManagerPolicy.UserRotationMode}.
-     */
-    private boolean mIgnoreRotationForApps;
-
-    /**
      * Keep track of wallpaper visibility to notify changes.
      */
     private boolean mLastWallpaperVisible = false;
@@ -618,6 +611,12 @@
      */
     private boolean mInEnsureActivitiesVisible = false;
 
+    /**
+     * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to
+     * prevent duplicate requests to input.
+     */
+    WindowState mLastRequestedFocus = null;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -1751,26 +1750,6 @@
 
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
-
-        // Not much of use to rotate the display for apps since it's close to square.
-        mIgnoreRotationForApps = isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height);
-    }
-
-    /** @return {@code true} if the orientation requested from application will be ignored. */
-    boolean ignoreRotationForApps() {
-        return mIgnoreRotationForApps;
-    }
-
-    private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) {
-        final DisplayCutout displayCutout =
-                calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
-        final int uiMode = mWmService.mPolicy.getUiMode();
-        final int w = mDisplayPolicy.getNonDecorDisplayWidth(
-                width, height, rotation, uiMode, displayCutout);
-        final int h = mDisplayPolicy.getNonDecorDisplayHeight(
-                width, height, rotation, uiMode, displayCutout);
-        final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h);
-        return aspectRatio <= mCloseToSquareMaxAspectRatio;
     }
 
     /**
@@ -2336,10 +2315,6 @@
     int getOrientation() {
         mLastOrientationSource = null;
 
-        if (mIgnoreRotationForApps) {
-            return SCREEN_ORIENTATION_USER;
-        }
-
         if (mWmService.mDisplayFrozen) {
             if (mWmService.mPolicy.isKeyguardLocked()) {
                 // Use the last orientation the while the display is frozen with the keyguard
@@ -4753,7 +4728,7 @@
             }
 
             // Apply restriction if necessary.
-            if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
+            if (needsGestureExclusionRestrictions(w, false /* ignoreRequest */)) {
 
                 // Processes the region along the left edge.
                 remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, outExclusion, leftEdge,
@@ -4770,7 +4745,7 @@
                 outExclusion.op(middle, Op.UNION);
                 middle.recycle();
             } else {
-                boolean loggable = needsGestureExclusionRestrictions(w, 0 /* lastSysUiVis */);
+                boolean loggable = needsGestureExclusionRestrictions(w, true /* ignoreRequest */);
                 if (loggable) {
                     addToGlobalAndConsumeLimit(local, outExclusion, leftEdge,
                             Integer.MAX_VALUE, w, EXCLUSION_LEFT);
@@ -4792,17 +4767,21 @@
     }
 
     /**
-     * @return Whether gesture exclusion area should be restricted from the window depending on the
-     *         current SystemUI visibility flags.
+     * Returns whether gesture exclusion area should be restricted from the window depending on the
+     * window/activity types and the requested navigation bar visibility and the behavior.
+     *
+     * @param win The target window.
+     * @param ignoreRequest If this is {@code true}, only the window/activity types are considered.
+     * @return {@code true} if the gesture exclusion restrictions are needed.
      */
-    private static boolean needsGestureExclusionRestrictions(WindowState win, int sysUiVisibility) {
+    private static boolean needsGestureExclusionRestrictions(WindowState win,
+            boolean ignoreRequest) {
         final int type = win.mAttrs.type;
-        final int stickyHideNavFlags =
-                SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
         final boolean stickyHideNav =
-                (sysUiVisibility & stickyHideNavFlags) == stickyHideNavFlags;
-        return !stickyHideNav && type != TYPE_INPUT_METHOD && type != TYPE_NOTIFICATION_SHADE
-                && win.getActivityType() != ACTIVITY_TYPE_HOME;
+                !win.getRequestedInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                        && win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+        return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD
+                && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME;
     }
 
     /**
@@ -4818,7 +4797,7 @@
                 && type != TYPE_APPLICATION_STARTING
                 && type != TYPE_NAVIGATION_BAR
                 && (attrs.flags & FLAG_NOT_TOUCHABLE) == 0
-                && needsGestureExclusionRestrictions(win, 0 /* sysUiVisibility */)
+                && needsGestureExclusionRestrictions(win, true /* ignoreRequest */)
                 && win.getDisplayContent().mDisplayPolicy.hasSideGestures();
     }
 
@@ -5293,6 +5272,14 @@
         mSingleTaskInstance = true;
     }
 
+    /**
+     * Check if the display has {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
+     */
+    boolean canShowWithInsecureKeyguard() {
+        final int flags = mDisplay.getFlags();
+        return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
+    }
+
     /** Returns true if the display can only contain one task */
     boolean isSingleTaskInstance() {
         return mSingleTaskInstance;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index c9f463b..6e32d0e 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -308,7 +308,7 @@
         }
 
         void tearDown() {
-            mService.mInputManager.unregisterInputChannel(mServerChannel);
+            mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
             mInputEventReceiver.dispose();
             mInputEventReceiver = null;
             mClientChannel.dispose();
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 5a24847..8d20bb8 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -192,7 +192,7 @@
 
         void onRemoved() {
             if (mInputChannel != null) {
-                mWmService.mInputManager.unregisterInputChannel(mInputChannel);
+                mWmService.mInputManager.unregisterInputChannel(mInputChannel.getToken());
                 mInputChannel.dispose();
                 mInputChannel = null;
             }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index e2c0749..251c014 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -22,12 +22,9 @@
 import android.annotation.Nullable;
 import android.util.Slog;
 
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
-
 /** Helper class to ensure activities are in the right visible state for a container. */
 class EnsureActivitiesVisibleHelper {
-    private final Task mContiner;
+    private final Task mTask;
     private ActivityRecord mTop;
     private ActivityRecord mStarting;
     private boolean mAboveTop;
@@ -38,11 +35,11 @@
     private boolean mNotifyClients;
 
     EnsureActivitiesVisibleHelper(Task container) {
-        mContiner = container;
+        mTask = container;
     }
 
     /**
-     * Update all attributes except {@link mContiner} to use in subsequent calculations.
+     * Update all attributes except {@link mTask} to use in subsequent calculations.
      *
      * @param starting The activity that is being started
      * @param configChanges Parts of the configuration that changed for this activity for evaluating
@@ -54,11 +51,11 @@
     void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
             boolean notifyClients) {
         mStarting = starting;
-        mTop = mContiner.topRunningActivity();
+        mTop = mTask.topRunningActivity();
         // If the top activity is not fullscreen, then we need to make sure any activities under it
         // are now visible.
         mAboveTop = mTop != null;
-        mContainerShouldBeVisible = mContiner.shouldBeVisible(mStarting);
+        mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting);
         mBehindFullscreenActivity = !mContainerShouldBeVisible;
         mConfigChanges = configChanges;
         mPreserveWindows = preserveWindows;
@@ -66,7 +63,26 @@
     }
 
     /**
-     * Ensure visibility with an option to also update the configuration of visible activities.
+     * Update visibility to activities.
+     * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @param starting The top most activity in the task.
+     *                 The activity is either starting or resuming.
+     *                 Caller should ensure starting activity is visible.
+     *
+     */
+    void processUpdate(@Nullable ActivityRecord starting) {
+        reset(starting, 0 /* configChanges */, false /* preserveWindows */,
+                false /* notifyClients */);
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible processUpdate behind " + mTop);
+        }
+
+        mTask.forAllActivities(this::updateActivityVisibility);
+    }
+
+    /**
+     * Commit visibility with an option to also update the configuration of visible activities.
      * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
      * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
      * @param starting The top most activity in the task.
@@ -79,54 +95,84 @@
      * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
      *                      be sent to the clients.
      */
-    void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
-            boolean notifyClients) {
+    void processCommit(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
         reset(starting, configChanges, preserveWindows, notifyClients);
 
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
-                + " configChanges=0x" + Integer.toHexString(configChanges));
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible processCommit behind " + mTop);
+        }
         if (mTop != null) {
-            mContiner.checkTranslucentActivityWaiting(mTop);
+            mTask.checkTranslucentActivityWaiting(mTop);
         }
 
         // We should not resume activities that being launched behind because these
         // activities are actually behind other fullscreen activities, but still required
         // to be visible (such as performing Recents animation).
         final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
-                && mContiner.isTopActivityFocusable()
-                && (starting == null || !starting.isDescendantOf(mContiner));
+                && mTask.isTopActivityFocusable()
+                && (starting == null || !starting.isDescendantOf(mTask));
 
-        final PooledConsumer f = PooledLambda.obtainConsumer(
-                EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
-                PooledLambda.__(ActivityRecord.class), starting, resumeTopActivity);
-        mContiner.forAllActivities(f);
-        f.recycle();
+        mTask.forAllActivities(a -> {
+            commitActivityVisibility(a, starting, resumeTopActivity);
+        });
     }
 
-    private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
-            final boolean resumeTopActivity) {
-        final boolean isTop = r == mTop;
+    private boolean isAboveTop(boolean isTop) {
         if (mAboveTop && !isTop) {
-            return;
+            return true;
         }
         mAboveTop = false;
+        return false;
+    }
 
-        final boolean reallyVisible = r.shouldBeVisible(
-                mBehindFullscreenActivity, false /* ignoringKeyguard */);
+    private void updateActivityVisibility(ActivityRecord r) {
+        final boolean isTop = r == mTop;
+        if (isAboveTop(isTop)) {
+            return;
+        }
+
+        r.updateVisibility(mBehindFullscreenActivity);
 
         // Check whether activity should be visible without Keyguard influence
         if (r.visibleIgnoringKeyguard) {
             if (r.occludesParent()) {
                 // At this point, nothing else needs to be shown in this task.
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
-                        + " stackVisible=" + mContainerShouldBeVisible
-                        + " behindFullscreen=" + mBehindFullscreenActivity);
+                if (DEBUG_VISIBILITY) {
+                    Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+                            + " stackVisible=" + mContainerShouldBeVisible
+                            + " behindFullscreen=" + mBehindFullscreenActivity);
+                }
                 mBehindFullscreenActivity = true;
             } else {
                 mBehindFullscreenActivity = false;
             }
         }
 
+        if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
+            if (DEBUG_VISIBILITY) {
+                Slog.v(TAG_VISIBILITY, "Home task: at " + mTask
+                        + " stackShouldBeVisible=" + mContainerShouldBeVisible
+                        + " behindFullscreenActivity=" + mBehindFullscreenActivity);
+            }
+            // No other task in the home stack should be visible behind the home activity.
+            // Home activities is usually a translucent activity with the wallpaper behind
+            // them. However, when they don't have the wallpaper behind them, we want to
+            // show activities in the next application stack behind them vs. another
+            // task in the home stack like recents.
+            mBehindFullscreenActivity = true;
+        }
+    }
+
+    private void commitActivityVisibility(ActivityRecord r, ActivityRecord starting,
+            final boolean resumeTopActivity) {
+        final boolean isTop = r == mTop;
+        if (isAboveTop(isTop)) {
+            return;
+        }
+
+        final boolean reallyVisible = r.shouldBeVisibleUnchecked();
+
         if (reallyVisible) {
             if (r.finishing) {
                 return;
@@ -170,20 +216,6 @@
                     + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
             r.makeInvisible();
         }
-
-        final int windowingMode = mContiner.getWindowingMode();
-        if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
-                && r.isRootOfTask()) {
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
-                    + " stackShouldBeVisible=" + mContainerShouldBeVisible
-                    + " behindFullscreenActivity=" + mBehindFullscreenActivity);
-            // No other task in the home stack should be visible behind the home activity.
-            // Home activities is usually a translucent activity with the wallpaper behind
-            // them. However, when they don't have the wallpaper behind them, we want to
-            // show activities in the next application stack behind them vs. another
-            // task in the home stack like recents.
-            mBehindFullscreenActivity = true;
-        }
     }
 
     private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
@@ -207,7 +239,7 @@
             r.setVisibility(true);
         }
         if (r != starting) {
-            mContiner.mStackSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
+            mTask.mStackSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index a79d3bb..1d1a266 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -156,7 +156,7 @@
     }
 
     void disposeChannelsLw(SurfaceControl.Transaction t) {
-        mService.mInputManager.unregisterInputChannel(mServerChannel);
+        mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
         mClientChannel.dispose();
         mServerChannel.dispose();
         t.remove(mInputSurface);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4efd687..714591a 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
@@ -275,7 +276,7 @@
 
     void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
             final WindowState child, int flags, final int type, final boolean isVisible,
-            final boolean hasFocus, final boolean hasWallpaper) {
+            final boolean focusable, final boolean hasWallpaper) {
         // Add a window to our list of input windows.
         inputWindowHandle.name = child.toString();
         flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
@@ -283,7 +284,7 @@
         inputWindowHandle.layoutParamsType = type;
         inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
         inputWindowHandle.visible = isVisible;
-        inputWindowHandle.focusable = hasFocus;
+        inputWindowHandle.focusable = focusable;
         inputWindowHandle.hasWallpaper = hasWallpaper;
         inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
         inputWindowHandle.ownerPid = child.mSession.mPid;
@@ -308,10 +309,13 @@
          * This means we need to make sure that these changes in crop are reflected
          * in the input windows, and so ensure this flag is set so that
          * the input crop always reflects the surface hierarchy.
-         * we may have some issues with modal-windows, but I guess we can
-         * cross that bridge when we come to implementing full-screen TaskOrg
+         *
+         * TODO(b/168252846): we have some issues with modal-windows, so we need to
+         * cross that bridge now that we organize full-screen Tasks.
          */
-        if (child.getTask() != null && child.getTask().isOrganized()) {
+        if (child.getTask() != null
+                && child.getTask().isOrganized()
+                && child.getTask().getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
             inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
         }
 
@@ -472,8 +476,9 @@
 
             resetInputConsumers(mInputTransaction);
 
-            mDisplayContent.forAllWindows(this,
-                    true /* traverseTopToBottom */);
+            mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
+
+            updateInputFocusRequest();
 
             if (!mUpdateInputWindowsImmediately) {
                 mDisplayContent.getPendingTransaction().merge(mInputTransaction);
@@ -483,6 +488,29 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
+        private void updateInputFocusRequest() {
+            if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) {
+                return;
+            }
+
+            final WindowState focus = mDisplayContent.mCurrentFocus;
+            if (focus == null || focus.mInputWindowHandle.token == null) {
+                mDisplayContent.mLastRequestedFocus = focus;
+                return;
+            }
+
+            if (!focus.mWinAnimator.hasSurface()) {
+                ProtoLog.d(WM_DEBUG_FOCUS_LIGHT,
+                        "Focus not requested for window=%s because it has no surface",
+                        focus);
+                return;
+            }
+
+            mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId);
+            mDisplayContent.mLastRequestedFocus = focus;
+            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
+        }
+
         @Override
         public void accept(WindowState w) {
             final InputChannel inputChannel = w.mInputChannel;
@@ -510,11 +538,12 @@
 
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
-            final boolean hasFocus = w.isFocused();
+            final boolean focusable = w.canReceiveKeys()
+                    && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
 
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
-                        mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
+                        mRecentsAnimationInputConsumer.mWindowHandle, focusable)) {
                     mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                     mAddRecentsAnimationInputConsumerHandle = false;
                 }
@@ -559,7 +588,7 @@
             }
 
             populateInputWindowHandle(
-                    inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+                    inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper);
 
             // register key interception info
             mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token,
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ab1074e..5520ad3 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -286,6 +286,7 @@
         }
         if (changed) {
             notifyInsetsChanged();
+            mDisplayContent.updateSystemGestureExclusion();
             mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw();
         }
     }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 69e8c57..bad28ba 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -278,6 +278,27 @@
     }
 
     /**
+     * Checks whether {@param r} should be visible depending on Keyguard state.
+     *
+     * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
+     */
+    boolean checkKeyguardVisibility(ActivityRecord r) {
+        if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
+            return true;
+        }
+
+        if (isKeyguardOrAodShowing(r.mDisplayContent.getDisplayId())) {
+            // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
+            // right away and AOD isn't visible.
+            return canShowActivityWhileKeyguardShowing(r, r.containsDismissKeyguardWindow());
+        } else if (isKeyguardLocked()) {
+            return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked());
+        } else {
+            return true;
+        }
+    }
+
+    /**
      * Makes sure to update lockscreen occluded/dismiss state if needed after completing all
      * visibility updates ({@link ActivityStackSupervisor#endActivityVisibilityUpdate}).
      */
@@ -442,6 +463,7 @@
         private final int mDisplayId;
         private boolean mOccluded;
         private ActivityRecord mDismissingKeyguardActivity;
+        private ActivityRecord mTopTurnScreenOnActivity;
         private boolean mRequestDismissKeyguard;
         private final ActivityTaskManagerService mService;
         private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
@@ -455,30 +477,38 @@
 
         void onRemoved() {
             mDismissingKeyguardActivity = null;
+            mTopTurnScreenOnActivity = null;
             mSleepTokenAcquirer.release(mDisplayId);
         }
 
         void visibilitiesUpdated(KeyguardController controller, DisplayContent display) {
             final boolean lastOccluded = mOccluded;
             final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
+            final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
             mRequestDismissKeyguard = false;
             mOccluded = false;
             mDismissingKeyguardActivity = null;
+            mTopTurnScreenOnActivity = null;
 
+            // only top + focusable + visible task can control occluding.
             final Task stack = getStackForControllingOccluding(display);
             if (stack != null) {
                 final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+                final ActivityRecord topTurnScreenOn = stack.getTopTurnScreenOnActivity();
                 mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
                         && stack.topRunningActivity() == topDismissing
                         && controller.canShowWhileOccluded(
                                 true /* dismissKeyguard */,
                                 false /* showWhenLocked */));
-                if (stack.getTopDismissingKeyguardActivity() != null) {
-                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+                if (topDismissing != null) {
+                    mDismissingKeyguardActivity = topDismissing;
+                }
+                if (topTurnScreenOn != null) {
+                    mTopTurnScreenOnActivity = topTurnScreenOn;
                 }
                 // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
-                if (mDisplayId != DEFAULT_DISPLAY) {
-                    mOccluded |= stack.canShowWithInsecureKeyguard()
+                if (mDisplayId != DEFAULT_DISPLAY && stack.mDisplayContent != null) {
+                    mOccluded |= stack.mDisplayContent.canShowWithInsecureKeyguard()
                             && controller.canDismissKeyguard();
                 }
             }
@@ -488,14 +518,20 @@
                         .getDisplayPolicy().isShowingDreamLw();
             }
 
-            if (lastOccluded != mOccluded) {
-                controller.handleOccludedChanged(mDisplayId);
-            }
-            if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
+            mRequestDismissKeyguard = lastDismissActivity != mDismissingKeyguardActivity
+                    && !mOccluded
                     && mDismissingKeyguardActivity != null
                     && controller.mWindowManager.isKeyguardSecure(
-                            controller.mService.getCurrentUserId())) {
-                mRequestDismissKeyguard = true;
+                    controller.mService.getCurrentUserId());
+
+            if (mTopTurnScreenOnActivity != null
+                    && mTopTurnScreenOnActivity != lastTurnScreenOnActivity
+                    && !mService.mWindowManager.mPowerManager.isInteractive()) {
+                controller.mStackSupervisor.wakeUp("handleTurnScreenOn");
+            }
+
+            if (lastOccluded != mOccluded) {
+                controller.handleOccludedChanged(mDisplayId);
             }
         }
 
@@ -525,6 +561,8 @@
             sb.append("  Occluded=").append(mOccluded)
                     .append(" DismissingKeyguardActivity=")
                     .append(mDismissingKeyguardActivity)
+                    .append(" TurnScreenOnActivity=")
+                    .append(mTopTurnScreenOnActivity)
                     .append(" at display=")
                     .append(mDisplayId);
             pw.println(sb.toString());
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index dccd3a6..4fe678d 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -239,7 +239,7 @@
         }
 
         void dispose() {
-            mWmService.mInputManager.unregisterInputChannel(mServerChannel);
+            mWmService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
             mInputEventReceiver.dispose();
             mServerChannel.dispose();
             mClientChannel.dispose();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6882dc4..b50cb4c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -814,14 +814,14 @@
     }
 
     boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
-            boolean hasFocus) {
+            boolean focusable) {
         // Update the input consumer touchable region to match the target app main window
         final WindowState targetAppMainWindow = mTargetActivityRecord != null
                 ? mTargetActivityRecord.findMainWindow()
                 : null;
         if (targetAppMainWindow != null) {
             targetAppMainWindow.getBounds(mTmpRect);
-            inputWindowHandle.focusable = hasFocus;
+            inputWindowHandle.focusable = focusable;
             inputWindowHandle.touchableRegion.set(mTmpRect);
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6539e13..d149db6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -99,6 +99,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.AppGlobals;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -1982,10 +1983,22 @@
                         notifyClients);
             }
         } finally {
-            mStackSupervisor.endActivityVisibilityUpdate();
+            mStackSupervisor.endActivityVisibilityUpdate(starting, configChanges, preserveWindows,
+                    notifyClients);
         }
     }
 
+    void commitActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        forAllTaskDisplayAreas(taskDisplayArea -> {
+            for (int stackNdx = taskDisplayArea.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final Task task = taskDisplayArea.getStackAt(stackNdx);
+                task.commitActivitiesVisible(starting, configChanges, preserveWindows,
+                        notifyClients);
+            }
+        });
+    }
+
     boolean switchUser(int userId, UserState uss) {
         final Task topFocusedStack = getTopDisplayFocusedStack();
         final int focusStackId = topFocusedStack != null
@@ -2201,7 +2214,22 @@
         ensureActivitiesVisible(null, 0, false /* preserveWindows */);
         resumeFocusedStacksTopActivities();
 
-        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+        notifyActivityPipModeChanged(r);
+    }
+
+    /**
+     * Notifies when an activity enters or leaves PIP mode.
+     * @param r indicates the activity currently in PIP, can be null to indicate no activity is
+     *          currently in PIP mode.
+     */
+    void notifyActivityPipModeChanged(@Nullable ActivityRecord r) {
+        final boolean inPip = r != null;
+        if (inPip) {
+            mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+        } else {
+            mService.getTaskChangeNotificationController().notifyActivityUnpinned();
+        }
+        mWindowManager.mPolicy.setPipVisibilityLw(inPip);
     }
 
     void executeAppTransitionForAllDisplay() {
@@ -2423,77 +2451,73 @@
         return display.getStack(windowingMode, activityType);
     }
 
-    private ActivityManager.StackInfo getStackInfo(Task stack) {
-        final TaskDisplayArea taskDisplayArea = stack.getDisplayArea();
-        ActivityManager.StackInfo info = new ActivityManager.StackInfo();
-        stack.getBounds(info.bounds);
-        info.displayId = taskDisplayArea != null ? taskDisplayArea.getDisplayId() : INVALID_DISPLAY;
-        info.stackId = stack.mTaskId;
-        info.stackToken = stack.mRemoteToken.toWindowContainerToken();
-        info.userId = stack.mCurrentUser;
-        info.visible = stack.shouldBeVisible(null);
-        // A stack might be not attached to a display.
-        // TODO: Can be removed since no one is using it.
-        info.position = taskDisplayArea != null ? taskDisplayArea.getIndexOf(stack) : 0;
-        info.configuration.setTo(stack.getConfiguration());
+    private RootTaskInfo getRootTaskInfo(Task task) {
+        final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
+        RootTaskInfo info = new RootTaskInfo();
+        task.fillTaskInfo(info);
 
-        final int numTasks = stack.getDescendantTaskCount();
-        info.taskIds = new int[numTasks];
-        info.taskNames = new String[numTasks];
-        info.taskBounds = new Rect[numTasks];
-        info.taskUserIds = new int[numTasks];
+        // A task might be not attached to a display.
+        info.position = taskDisplayArea != null ? taskDisplayArea.getIndexOf(task) : 0;
+        info.visible = task.shouldBeVisible(null);
+        task.getBounds(info.bounds);
+
+        final int numTasks = task.getDescendantTaskCount();
+        info.childTaskIds = new int[numTasks];
+        info.childTaskNames = new String[numTasks];
+        info.childTaskBounds = new Rect[numTasks];
+        info.childTaskUserIds = new int[numTasks];
         final int[] currentIndex = {0};
 
         final PooledConsumer c = PooledLambda.obtainConsumer(
-                RootWindowContainer::processTaskForStackInfo, PooledLambda.__(Task.class), info,
+                RootWindowContainer::processTaskForTaskInfo, PooledLambda.__(Task.class), info,
                 currentIndex);
-        stack.forAllLeafTasks(c, false /* traverseTopToBottom */);
+        task.forAllLeafTasks(c, false /* traverseTopToBottom */);
         c.recycle();
 
-        final ActivityRecord top = stack.topRunningActivity();
+        final ActivityRecord top = task.topRunningActivity();
         info.topActivity = top != null ? top.intent.getComponent() : null;
         return info;
     }
 
-    private static void processTaskForStackInfo(
-            Task task, ActivityManager.StackInfo info, int[] currentIndex) {
+    private static void processTaskForTaskInfo(
+            Task task, RootTaskInfo info, int[] currentIndex) {
         int i = currentIndex[0];
-        info.taskIds[i] = task.mTaskId;
-        info.taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+        info.childTaskIds[i] = task.mTaskId;
+        info.childTaskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
                 : task.realActivity != null ? task.realActivity.flattenToString()
                         : task.getTopNonFinishingActivity() != null
                                 ? task.getTopNonFinishingActivity().packageName : "unknown";
-        info.taskBounds[i] = task.mAtmService.getTaskBounds(task.mTaskId);
-        info.taskUserIds[i] = task.mUserId;
+        info.childTaskBounds[i] = task.mAtmService.getTaskBounds(task.mTaskId);
+        info.childTaskUserIds[i] = task.mUserId;
         currentIndex[0] = ++i;
     }
 
-    ActivityManager.StackInfo getStackInfo(int stackId) {
-        Task stack = getStack(stackId);
-        if (stack != null) {
-            return getStackInfo(stack);
+    RootTaskInfo getRootTaskInfo(int taskId) {
+        Task task = getStack(taskId);
+        if (task != null) {
+            return getRootTaskInfo(task);
         }
         return null;
     }
 
-    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+    RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) {
         final Task stack = getStack(windowingMode, activityType);
-        return (stack != null) ? getStackInfo(stack) : null;
+        return (stack != null) ? getRootTaskInfo(stack) : null;
     }
 
-    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType, int displayId) {
+    RootTaskInfo getRootTaskInfo(int windowingMode, int activityType, int displayId) {
         final Task stack = getStack(windowingMode, activityType, displayId);
-        return (stack != null) ? getStackInfo(stack) : null;
+        return (stack != null) ? getRootTaskInfo(stack) : null;
     }
 
-    /** If displayId == INVALID_DISPLAY, this will get stack infos on all displays */
-    ArrayList<ActivityManager.StackInfo> getAllStackInfos(int displayId) {
-        ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
+    /** If displayId == INVALID_DISPLAY, this will get root task infos on all displays */
+    ArrayList<RootTaskInfo> getAllRootTaskInfos(int displayId) {
+        ArrayList<RootTaskInfo> list = new ArrayList<>();
         if (displayId == INVALID_DISPLAY) {
             forAllTaskDisplayAreas(taskDisplayArea -> {
                 for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
                     final Task stack = taskDisplayArea.getStackAt(sNdx);
-                    list.add(getStackInfo(stack));
+                    list.add(getRootTaskInfo(stack));
                 }
             });
             return list;
@@ -2505,7 +2529,7 @@
         display.forAllTaskDisplayAreas(taskDisplayArea -> {
             for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
                 final Task stack = taskDisplayArea.getStackAt(sNdx);
-                list.add(getStackInfo(stack));
+                list.add(getRootTaskInfo(stack));
             }
         });
         return list;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3b32a9d76..3d6d7b7 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -56,6 +56,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.protolog.common.ProtoLog;
@@ -201,9 +202,7 @@
     @Override
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
-            Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
-            Rect outStableInsets, Rect outBackdropFrame,
-            DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
+            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
             SurfaceControl outBLASTSurfaceControl) {
@@ -212,10 +211,8 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
-                outFrame, outContentInsets, outVisibleInsets,
-                outStableInsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls,
-                outSurfaceSize, outBLASTSurfaceControl);
+                outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
+                outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
@@ -240,11 +237,6 @@
     }
 
     @Override
-    public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
-        mService.getWindowDisplayFrame(this, window, outDisplayFrame);
-    }
-
-    @Override
     public void finishDrawing(IWindow window,
             @Nullable SurfaceControl.Transaction postDrawTransaction) {
         if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c35d732..15a44e8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -61,7 +61,6 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.SurfaceControl.METADATA_TASK_ID;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
@@ -197,7 +196,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
@@ -600,6 +598,7 @@
 
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
+    private ActivityRecord mTopTurnScreenOnActivity;
 
     private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
 
@@ -1394,7 +1393,8 @@
         }
 
         if (display != null) {
-            // TODO(NOW!): Chat with the erosky@ of this code to see if this really makes sense here...
+            // TODO(b/168037178): Chat with the erosky@ of this code to see if this really makes
+            //                    sense here...
             // Rotations are relative to the display. This means if there are 2 displays rotated
             // differently (eg. 2 monitors with one landscape and one portrait), moving a stack
             // from one to the other could look like a rotation change. To prevent this
@@ -1408,11 +1408,16 @@
 
         super.onParentChanged(newParent, oldParent);
 
-        // TODO(NOW): The check for null display content and setting it to null doesn't really
-        //  make sense here...
+        // Call this again after super onParentChanged in-case the surface wasn't created yet
+        // (happens when the task is first inserted into the hierarchy). It's a no-op if it
+        // already ran fully within super.onParentChanged
+        updateTaskOrganizerState(false /* forceUpdate */);
 
-        // TODO(stack-merge): This is mostly taking care of the case where the stask is removing from
-        // the display, so we should probably consolidate it there instead.
+        // TODO(b/168037178): The check for null display content and setting it to null doesn't
+        //                    really make sense here...
+
+        // TODO(b/168037178): This is mostly taking care of the case where the stask is removing
+        //                    from the display, so we should probably consolidate it there instead.
 
         if (getParent() == null && mDisplayContent != null) {
             EventLogTags.writeWmStackRemoved(getRootTaskId());
@@ -1434,7 +1439,7 @@
                     && (newParent == null || !newParent.inPinnedWindowingMode())) {
                 // Notify if a task from the pinned stack is being removed
                 // (or moved depending on the mode).
-                mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+                mRootWindowContainer.notifyActivityPipModeChanged(null);
             }
         }
 
@@ -4019,7 +4024,7 @@
      */
     void fillTaskInfo(TaskInfo info, boolean stripExtras) {
         getNumRunningActivities(mReuseActivitiesReport);
-        info.userId = mUserId;
+        info.userId = isLeafTask() ? mUserId : mCurrentUser;
         info.stackId = getRootTaskId();
         info.taskId = mTaskId;
         info.displayId = getDisplayId();
@@ -4850,6 +4855,11 @@
      * @return {@code true} if task organizer changed.
      */
     boolean updateTaskOrganizerState(boolean forceUpdate) {
+        if (getSurfaceControl() == null) {
+            // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
+            // is created.
+            return false;
+        }
         if (!isRootTask()) {
             return setTaskOrganizer(null);
         }
@@ -5102,10 +5112,11 @@
                     : WINDOWING_MODE_FULLSCREEN;
         }
         if (currentMode == WINDOWING_MODE_PINNED) {
-            mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+            mRootWindowContainer.notifyActivityPipModeChanged(null);
         }
         if (likelyResolvedMode == WINDOWING_MODE_PINNED
                 && taskDisplayArea.getRootPinnedTask() != null) {
+
             // Can only have 1 pip at a time, so replace an existing pip
             taskDisplayArea.getRootPinnedTask().dismissPip();
         }
@@ -5663,21 +5674,29 @@
             boolean preserveWindows, boolean notifyClients) {
         mTopActivityOccludesKeyguard = false;
         mTopDismissingKeyguardActivity = null;
+        mTopTurnScreenOnActivity = null;
         mStackSupervisor.beginActivityVisibilityUpdate();
         try {
-            mEnsureActivitiesVisibleHelper.process(
-                    starting, configChanges, preserveWindows, notifyClients);
+            mEnsureActivitiesVisibleHelper.processUpdate(starting);
 
             if (mTranslucentActivityWaiting != null &&
                     mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
-                // Nothing is getting drawn or everything was already visible, don't wait for timeout.
+                // Nothing is getting drawn or everything was already visible, don't wait for
+                // timeout.
                 notifyActivityDrawnLocked(null);
             }
         } finally {
-            mStackSupervisor.endActivityVisibilityUpdate();
+            mStackSupervisor.endActivityVisibilityUpdate(starting, configChanges, preserveWindows,
+                    notifyClients);
         }
     }
 
+    void commitActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        mEnsureActivitiesVisibleHelper.processCommit(starting, configChanges, preserveWindows,
+                notifyClients);
+    }
+
     /**
      * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
      */
@@ -5711,64 +5730,34 @@
     }
 
     /**
-     * Checks whether {@param r} should be visible depending on Keyguard state and updates
-     * {@link #mTopActivityOccludesKeyguard} and {@link #mTopDismissingKeyguardActivity} if
-     * necessary.
-     *
-     * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
+     * @return the top most visible activity that wants to turn screen on
      */
-    boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
-        int displayId = getDisplayId();
-        if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
-
-        final boolean keyguardOrAodShowing = mStackSupervisor.getKeyguardController()
-                .isKeyguardOrAodShowing(displayId);
-        final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
-        final boolean showWhenLocked = r.canShowWhenLocked();
-        final boolean dismissKeyguard = r.containsDismissKeyguardWindow();
-        if (shouldBeVisible) {
-            if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
-                mTopDismissingKeyguardActivity = r;
-            }
-
-            // Only the top activity may control occluded, as we can't occlude the Keyguard if the
-            // top app doesn't want to occlude it.
-            if (isTop) {
-                mTopActivityOccludesKeyguard |= showWhenLocked;
-            }
-
-            final boolean canShowWithKeyguard = canShowWithInsecureKeyguard()
-                    && mStackSupervisor.getKeyguardController().canDismissKeyguard();
-            if (canShowWithKeyguard) {
-                return true;
-            }
-        }
-        if (keyguardOrAodShowing) {
-            // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
-            // right away and AOD isn't visible.
-            return shouldBeVisible && mStackSupervisor.getKeyguardController()
-                    .canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
-        } else if (keyguardLocked) {
-            return shouldBeVisible && mStackSupervisor.getKeyguardController().canShowWhileOccluded(
-                    dismissKeyguard, showWhenLocked);
-        } else {
-            return shouldBeVisible;
-        }
+    ActivityRecord getTopTurnScreenOnActivity() {
+        return mTopTurnScreenOnActivity;
     }
 
     /**
-     * Check if the display to which this stack is attached has
-     * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
+     * Updates {@link #mTopActivityOccludesKeyguard}, {@link #mTopTurnScreenOnActivity} and
+     * {@link #mTopDismissingKeyguardActivity} if this task could be visible.
+     *
      */
-    boolean canShowWithInsecureKeyguard() {
-        final DisplayContent displayContent = mDisplayContent;
-        if (displayContent == null) {
-            throw new IllegalStateException("Stack is not attached to any display, stackId="
-                    + getRootTaskId());
+    void updateKeyguardVisibility(ActivityRecord r, boolean isTop) {
+        final boolean showWhenLocked = r.canShowWhenLocked();
+        final boolean dismissKeyguard = r.containsDismissKeyguardWindow();
+        final boolean turnScreenOn = r.canTurnScreenOn();
+        if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
+            mTopDismissingKeyguardActivity = r;
         }
 
-        final int flags = displayContent.mDisplay.getFlags();
-        return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
+        if (turnScreenOn && mTopTurnScreenOnActivity == null) {
+            mTopTurnScreenOnActivity = r;
+        }
+
+        // Only the top activity may control occluded, as we can't occlude the Keyguard if the
+        // top app doesn't want to occlude it.
+        if (isTop) {
+            mTopActivityOccludesKeyguard |= showWhenLocked;
+        }
     }
 
     void checkTranslucentActivityWaiting(ActivityRecord top) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 6550167..2b32e40 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1779,7 +1779,8 @@
                         notifyClients);
             }
         } finally {
-            mAtmService.mStackSupervisor.endActivityVisibilityUpdate();
+            mAtmService.mStackSupervisor.endActivityVisibilityUpdate(starting, configChanges,
+                    preserveWindows, notifyClients);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index d9290fb..e07c567 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -18,10 +18,8 @@
 
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
@@ -73,11 +71,9 @@
     // The set of modes that are currently supports
     // TODO: Remove once the task organizer can support all modes
     @VisibleForTesting
-    static final int[] SUPPORTED_WINDOWING_MODES = {
-            WINDOWING_MODE_PINNED,
-            WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-            WINDOWING_MODE_MULTI_WINDOW,
+    static final int[] UNSUPPORTED_WINDOWING_MODES = {
+            WINDOWING_MODE_UNDEFINED,
+            WINDOWING_MODE_FREEFORM
     };
 
     private final WindowManagerGlobalLock mGlobalLock;
@@ -311,18 +307,17 @@
             synchronized (mGlobalLock) {
                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
                         organizer.asBinder(), uid);
-                for (int winMode : SUPPORTED_WINDOWING_MODES) {
-                    if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
-                        mTaskOrganizers.add(organizer);
-                        mTaskOrganizerStates.put(organizer.asBinder(),
-                                new TaskOrganizerState(organizer, uid));
-                    }
-                    mService.mRootWindowContainer.forAllTasks((task) -> {
-                        if (task.getWindowingMode() == winMode) {
-                            task.updateTaskOrganizerState(true /* forceUpdate */);
-                        }
-                    });
+                if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
+                    mTaskOrganizers.add(organizer);
+                    mTaskOrganizerStates.put(organizer.asBinder(),
+                            new TaskOrganizerState(organizer, uid));
                 }
+                mService.mRootWindowContainer.forAllTasks((task) -> {
+                    if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
+                        return;
+                    }
+                    task.updateTaskOrganizerState(true /* forceUpdate */);
+                });
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -360,12 +355,7 @@
     }
 
     private boolean isSupportedWindowingMode(int winMode) {
-        for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
-            if (SUPPORTED_WINDOWING_MODES[i] == winMode) {
-                return true;
-            }
-        }
-        return false;
+        return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
     }
 
     void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -659,21 +649,18 @@
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.print(prefix); pw.println("TaskOrganizerController:");
-        pw.print(innerPrefix); pw.println("Per windowing mode:");
-        for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
-            final int windowingMode = SUPPORTED_WINDOWING_MODES[i];
-            pw.println(innerPrefix + "  "
-                    + WindowConfiguration.windowingModeToString(windowingMode) + ":");
-            for (final TaskOrganizerState state : mTaskOrganizerStates.values()) {
-                final ArrayList<Task> tasks = state.mOrganizedTasks;
-                pw.print(innerPrefix + "    ");
-                pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
-                for (int k = 0; k < tasks.size(); k++) {
-                    final Task task = tasks.get(k);
-                    if (windowingMode == task.getWindowingMode()) {
-                        pw.println(innerPrefix + "      " + task);
-                    }
+        for (final TaskOrganizerState state : mTaskOrganizerStates.values()) {
+            final ArrayList<Task> tasks = state.mOrganizedTasks;
+            pw.print(innerPrefix + "  ");
+            pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
+            for (int k = 0; k < tasks.size(); k++) {
+                final Task task = tasks.get(k);
+                final int mode = task.getWindowingMode();
+                if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, mode)) {
+                    continue;
                 }
+                pw.println(innerPrefix + "    ("
+                        + WindowConfiguration.windowingModeToString(mode) + ") " + task);
             }
 
         }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index f32781a..abe6329 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -299,7 +299,7 @@
         }
 
         mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
-        mService.mInputManager.unregisterInputChannel(mServerChannel);
+        mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
 
         mInputEventReceiver.dispose();
         mInputEventReceiver = null;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index e9ada6b..100f1bf 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -67,7 +67,6 @@
 import android.os.SystemClock;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
@@ -79,6 +78,7 @@
 import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -159,12 +159,8 @@
         final IWindowSession session = WindowManagerGlobal.getWindowSession();
         window.setSession(session);
         final SurfaceControl surfaceControl = new SurfaceControl();
-        final Rect tmpRect = new Rect();
-        final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
-        final Rect tmpFrame = new Rect();
+        final ClientWindowFrames tmpFrames = new ClientWindowFrames();
         final Rect taskBounds;
-        final Rect tmpContentInsets = new Rect();
-        final Rect tmpStableInsets = new Rect();
         final InsetsState mTmpInsetsState = new InsetsState();
         final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
@@ -254,8 +250,9 @@
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
-                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect,
-                    tmpRect, tmpCutout, null, mTmpInsetsState, mTempControls);
+                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrames.frame,
+                    tmpFrames.contentInsets, tmpFrames.stableInsets, tmpFrames.displayCutout,
+                    null /* outInputChannel */, mTmpInsetsState, mTempControls);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -270,15 +267,14 @@
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
-                    tmpFrame, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
+                    tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
                     mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
         } catch (RemoteException e) {
             // Local call.
         }
 
-        final Rect systemBarInsets = getSystemBarInsets(tmpFrame, insetsState);
-        snapshotSurface.setFrames(tmpFrame, systemBarInsets);
+        final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, insetsState);
+        snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
         snapshotSurface.drawSnapshot();
         return snapshotSurface;
     }
@@ -528,11 +524,9 @@
         }
 
         @Override
-        public void resized(Rect frame, Rect contentInsets, Rect visibleInsets,
-                Rect stableInsets, boolean reportDraw,
-                MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeSystemBars, int displayId,
-                DisplayCutout.ParcelableWrapper displayCutout) {
+        public void resized(ClientWindowFrames frames, boolean reportDraw,
+                MergedConfiguration mergedConfiguration, boolean forceLayout,
+                boolean alwaysConsumeSystemBars, int displayId) {
             if (mergedConfiguration != null && mOuter != null
                     && mOuter.mOrientationOnCreation
                             != mergedConfiguration.getMergedConfiguration().orientation) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8a5e70f..ae8f7a5 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -2464,8 +2465,9 @@
 
     private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
                                     boolean isVoiceInteraction) {
-        if (isOrganized()) {
-            // Defer to the task organizer to run animations
+        if (isOrganized()
+                // TODO(b/161711458): Clean-up when moved to shell.
+                && getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
             return null;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 017747f..8eb6432 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -258,6 +258,7 @@
 import android.view.WindowManager.TransitionType;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -2065,21 +2066,6 @@
         }
     }
 
-    public void getWindowDisplayFrame(Session session, IWindow client,
-            Rect outDisplayFrame) {
-        synchronized (mGlobalLock) {
-            WindowState win = windowForClientLocked(session, client, false);
-            if (win == null) {
-                outDisplayFrame.setEmpty();
-                return;
-            }
-            outDisplayFrame.set(win.getDisplayFrame());
-            if (win.inSizeCompatMode()) {
-                outDisplayFrame.scale(win.mInvGlobalScale);
-            }
-        }
-    }
-
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
         synchronized (mGlobalLock) {
             if (mAccessibilityController != null) {
@@ -2115,9 +2101,7 @@
 
     public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility, int flags,
-            long frameNumber, Rect outFrame, Rect outContentInsets,
-            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
-            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
+            long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
             SurfaceControl outBLASTSurfaceControl) {
@@ -2386,10 +2370,6 @@
             if (win.mActivityRecord != null) {
                 win.mActivityRecord.updateReportedVisibilityLocked();
             }
-            if (winAnimator.mReportSurfaceResized) {
-                winAnimator.mReportSurfaceResized = false;
-                result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
-            }
             if (displayPolicy.areSystemBarsForcedShownLw(win)) {
                 result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
             }
@@ -2419,18 +2399,14 @@
             // The last inset values represent the last client state
             win.updateLastInsetValues();
 
-            win.getCompatFrame(outFrame);
-            win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
-                    outStableInsets);
-            outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
-            outBackdropFrame.set(win.getBackdropFrame(win.getFrame()));
+            win.fillClientWindowFrames(outFrames);
             outInsetsState.set(win.getInsetsState(), win.isClientLocal());
             if (DEBUG) {
                 Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
                         + ", requestedWidth=" + requestedWidth
                         + ", requestedHeight=" + requestedHeight
                         + ", viewVisibility=" + viewVisibility
-                        + "\nRelayout returning frame=" + outFrame
+                        + "\nRelayout returning frame=" + outFrames.frame
                         + ", surface=" + outSurfaceControl);
             }
 
@@ -2440,8 +2416,7 @@
             result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
 
             if (DEBUG_LAYOUT) {
-                Slog.v(TAG_WM,
-                        "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
+                Slog.v(TAG_WM, "Relayout complete " + win + ": outFrames=" + outFrames);
             }
             win.mInRelayout = false;
 
@@ -5753,19 +5728,6 @@
     }
 
     @Override
-    public void setPipVisibility(boolean visible) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Caller does not hold permission "
-                    + android.Manifest.permission.STATUS_BAR);
-        }
-
-        synchronized (mGlobalLock) {
-            mPolicy.setPipVisibilityLw(visible);
-        }
-    }
-
-    @Override
     public void statusBarVisibilityChanged(int displayId, int visibility) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 84a9c75..cf73def 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -209,7 +209,6 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
-import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IApplicationToken;
@@ -233,6 +232,7 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.KeyInterceptionInfo;
@@ -388,15 +388,6 @@
     private final Configuration mTempConfiguration = new Configuration();
 
     /**
-     * The last content insets returned to the client in relayout. We use
-     * these in the bounds animation to ensure we only observe inset changes
-     * at the same time that a client resizes it's surface so that we may use
-     * the geometryAppliesWithResize synchronization mechanism to keep
-     * the contents in place.
-     */
-    final Rect mLastRelayoutContentInsets = new Rect();
-
-    /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
      */
@@ -437,6 +428,8 @@
 
     private final WindowFrames mWindowFrames = new WindowFrames();
 
+    private final ClientWindowFrames mClientWindowFrames = new ClientWindowFrames();
+
     /** The frames used to compute a temporal layout appearance. */
     private WindowFrames mSimulatedWindowFrames;
 
@@ -1299,7 +1292,10 @@
         return mWindowFrames.mRelFrame;
     }
 
-    /** Retrieves the frame of the display that this window was last laid out in. */
+    /**
+     * Gets the frame that excludes the area of side insets according to the layout parameter from
+     * {@link WindowManager.LayoutParams#setFitInsetsSides}.
+     */
     Rect getDisplayFrame() {
         return mWindowFrames.mDisplayFrame;
     }
@@ -2504,7 +2500,7 @@
 
         // unregister server channel first otherwise it complains about broken channel
         if (mInputChannel != null) {
-            mWmService.mInputManager.unregisterInputChannel(mInputChannel);
+            mWmService.mInputManager.unregisterInputChannel(mInputChannel.getToken());
 
             mInputChannel.dispose();
             mInputChannel = null;
@@ -3585,6 +3581,39 @@
         return wpc != null && wpc.registeredForDisplayConfigChanges();
     }
 
+    void fillClientWindowFrames(ClientWindowFrames outFrames) {
+        outFrames.frame.set(mWindowFrames.mCompatFrame);
+        outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
+        if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
+            outFrames.displayFrame.scale(mInvGlobalScale);
+        }
+
+        final Rect backdropFrame = outFrames.backdropFrame;
+        // When the task is docked, we send fullscreen sized backdropFrame as soon as resizing
+        // start even if we haven't received the relayout window, so that the client requests
+        // the relayout sooner. When dragging stops, backdropFrame needs to stay fullscreen
+        // until the window to small size, otherwise the multithread renderer will shift last
+        // one or more frame to wrong offset. So here we send fullscreen backdrop if either
+        // isDragResizing() or isDragResizeChanged() is true.
+        final boolean resizing = isDragResizing() || isDragResizeChanged();
+        if (!resizing || getWindowConfiguration().useWindowFrameForBackdrop()) {
+            // Surface position is now inherited from parent, and BackdropFrameRenderer uses
+            // backdrop frame to position content. Thus we just keep the size of backdrop frame,
+            // and remove the offset to avoid double offset from display origin.
+            backdropFrame.set(outFrames.frame);
+            backdropFrame.offsetTo(0, 0);
+        } else {
+            final DisplayInfo displayInfo = getDisplayInfo();
+            backdropFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+        }
+        outFrames.displayCutout.set(mWindowFrames.mDisplayCutout.getDisplayCutout());
+
+        // TODO(b/149813814): Remove legacy insets.
+        outFrames.contentInsets.set(mWindowFrames.mLastContentInsets);
+        outFrames.visibleInsets.set(mWindowFrames.mLastVisibleInsets);
+        outFrames.stableInsets.set(mWindowFrames.mLastStableInsets);
+    }
+
     void reportResized() {
         // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
         // since it will be destroyed anyway. This also prevents the client from receiving
@@ -3616,23 +3645,18 @@
         mWinAnimator.mSurfaceResized = false;
         mWindowFrames.resetInsetsChanged();
 
-        final Rect frame = mWindowFrames.mCompatFrame;
-        final Rect contentInsets = mWindowFrames.mLastContentInsets;
-        final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
-        final Rect stableInsets = mWindowFrames.mLastStableInsets;
         final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
         final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync() || !mRedrawForSyncReported;
         final boolean forceRelayout = reportOrientation || isDragResizeChanged() || !mRedrawForSyncReported;
         final int displayId = getDisplayId();
-        final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
+        fillClientWindowFrames(mClientWindowFrames);
 
         mRedrawForSyncReported = true;
 
         try {
-            mClient.resized(frame, contentInsets, visibleInsets, stableInsets, reportDraw,
-                    mergedConfiguration, getBackdropFrame(frame), forceRelayout,
+            mClient.resized(mClientWindowFrames, reportDraw, mergedConfiguration, forceRelayout,
                     getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this),
-                    displayId, new DisplayCutout.ParcelableWrapper(displayCutout));
+                    displayId);
 
             if (mWmService.mAccessibilityController != null) {
                 mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId);
@@ -3738,27 +3762,6 @@
         return (mAttrs.insetsFlags.behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
     }
 
-    Rect getBackdropFrame(Rect frame) {
-        // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
-        // start even if we haven't received the relayout window, so that the client requests
-        // the relayout sooner. When dragging stops, backDropFrame needs to stay fullscreen
-        // until the window to small size, otherwise the multithread renderer will shift last
-        // one or more frame to wrong offset. So here we send fullscreen backdrop if either
-        // isDragResizing() or isDragResizeChanged() is true.
-        boolean resizing = isDragResizing() || isDragResizeChanged();
-        if (getWindowConfiguration().useWindowFrameForBackdrop() || !resizing) {
-            // Surface position is now inherited from parent, and BackdropFrameRenderer uses
-            // backdrop frame to position content. Thus we just keep the size of backdrop frame, and
-            // remove the offset to avoid double offset from display origin.
-            mTmpRect.set(frame);
-            mTmpRect.offsetTo(0, 0);
-            return mTmpRect;
-        }
-        final DisplayInfo displayInfo = getDisplayInfo();
-        mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-        return mTmpRect;
-    }
-
     private int getRootTaskId() {
         final Task stack = getRootTask();
         if (stack == null) {
@@ -5670,18 +5673,6 @@
         }
     }
 
-    /**
-     * Copy the inset values over so they can be sent back to the client when a relayout occurs.
-     */
-    void getInsetsForRelayout(Rect outContentInsets, Rect outVisibleInsets,
-            Rect outStableInsets) {
-        outContentInsets.set(mWindowFrames.mContentInsets);
-        outVisibleInsets.set(mWindowFrames.mVisibleInsets);
-        outStableInsets.set(mWindowFrames.mStableInsets);
-
-        mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
-    }
-
     void getContentInsets(Rect outContentInsets) {
         outContentInsets.set(mWindowFrames.mContentInsets);
     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1bd712c..5e81400 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -126,11 +126,7 @@
      * we must tell them application to resize (and thus redraw itself).
      */
     boolean mSurfaceResized;
-    /**
-     * Whether we should inform the client on next relayoutWindow that
-     * the surface has been resized since last time.
-     */
-    boolean mReportSurfaceResized;
+
     WindowSurfaceController mSurfaceController;
     private WindowSurfaceController mPendingDestroySurface;
 
@@ -872,7 +868,6 @@
         }
 
         if (mSurfaceResized) {
-            mReportSurfaceResized = true;
             mWin.getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
         }
     }
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 7e9e11d..95d4ba7 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -29,6 +29,7 @@
 
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <binder/IPCThreadState.h>
 #include <jni.h>
 #include <processgroup/processgroup.h>
 
@@ -90,11 +91,20 @@
     }
 }
 
+static void com_android_server_am_CachedAppOptimizer_freezeBinder(
+        JNIEnv *env, jobject clazz, jint pid, jboolean freeze) {
+
+    if (IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */) != 0) {
+        jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
+    }
+}
+
 static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
     {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
     {"enableFreezerInternal", "(Z)V",
         (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
+    {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}
 };
 
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5dd6cd7..46136ca 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -209,7 +209,7 @@
     status_t registerInputChannel(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel);
     status_t registerInputMonitor(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel,
                                   int32_t displayId, bool isGestureMonitor);
-    status_t unregisterInputChannel(JNIEnv* env, const InputChannel& inputChannel);
+    status_t unregisterInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken);
     status_t pilferPointers(const sp<IBinder>& token);
 
     void displayRemoved(JNIEnv* env, int32_t displayId);
@@ -447,9 +447,9 @@
 }
 
 status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
-                                                    const InputChannel& inputChannel) {
+                                                    const sp<IBinder>& connectionToken) {
     ATRACE_CALL();
-    return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
+    return mInputManager->getDispatcher()->unregisterInputChannel(connectionToken);
 }
 
 status_t NativeInputManager::pilferPointers(const sp<IBinder>& token) {
@@ -1364,7 +1364,7 @@
 
     ALOGW("Input channel object '%s' was disposed without first being unregistered with "
             "the input manager!", inputChannel->getName().c_str());
-    im->unregisterInputChannel(env, *inputChannel);
+    im->unregisterInputChannel(env, inputChannel->getConnectionToken());
 }
 
 static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
@@ -1417,20 +1417,12 @@
     }
 }
 
-static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobject inputChannelObj) {
+static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                         jobject tokenObj) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
 
-    std::shared_ptr<InputChannel> inputChannel =
-            android_view_InputChannel_getInputChannel(env, inputChannelObj);
-    if (inputChannel == nullptr) {
-        throwInputChannelNotInitialized(env);
-        return;
-    }
-
-    android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr);
-
-    status_t status = im->unregisterInputChannel(env, *inputChannel);
+    status_t status = im->unregisterInputChannel(env, token);
     if (status && status != BAD_VALUE) { // ignore already unregistered channel
         std::string message;
         message += StringPrintf("Failed to unregister input channel.  status=%d", status);
@@ -1792,7 +1784,7 @@
          (void*)nativeRegisterInputChannel},
         {"nativeRegisterInputMonitor", "(JLandroid/view/InputChannel;IZ)V",
          (void*)nativeRegisterInputMonitor},
-        {"nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
+        {"nativeUnregisterInputChannel", "(JLandroid/os/IBinder;)V",
          (void*)nativeUnregisterInputChannel},
         {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
         {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0d3d73f..e85bbd9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2159,6 +2159,11 @@
         return getDeviceOwnerOfCallerLocked(caller);
     }
 
+    @NonNull ActiveAdmin getParentOfAdminIfRequired(ActiveAdmin admin, boolean parent) {
+        Objects.requireNonNull(admin);
+        return parent ? admin.getParentActiveAdmin() : admin;
+    }
+
     /**
      * Finds an active admin for the caller then checks {@code permission} if admin check failed.
      *
@@ -4527,6 +4532,8 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
         // timeoutMs with value 0 means that the admin doesn't participate
         // timeoutMs is clamped to the interval in case the internal constants change in the future
         final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs();
@@ -4537,11 +4544,11 @@
             timeoutMs = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
         }
 
-        final int userHandle = mInjector.userHandleGetCallingUserId();
+        final int userHandle = caller.getUserId();
         boolean changed = false;
         synchronized (getLockObject()) {
-            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            ActiveAdmin ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller),
+                    parent);
             if (ap.strongAuthUnlockTimeout != timeoutMs) {
                 ap.strongAuthUnlockTimeout = timeoutMs;
                 saveSettingsLocked(userHandle);
@@ -5646,8 +5653,9 @@
             List<String> lockdownWhitelist)
             throws SecurityException {
         enforceProfileOrDeviceOwner(who);
+        final CallerIdentity caller = getCallerIdentity(who);
 
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final int userId = caller.getUserId();
         mInjector.binderWithCleanCallingIdentity(() -> {
             if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
                 Slog.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
@@ -5678,8 +5686,7 @@
                     .write();
         });
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
             if (!TextUtils.equals(vpnPackage, admin.mAlwaysOnVpnPackage)
                     || lockdown != admin.mAlwaysOnVpnLockdown) {
                 admin.mAlwaysOnVpnPackage = vpnPackage;
@@ -9680,10 +9687,11 @@
             return null;
         }
         Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
 
         synchronized (getLockObject()) {
-            final ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+                    getProfileOwnerOrDeviceOwnerLocked(caller), parent);
             if (parent) {
                 enforceProfileOwnerOfOrganizationOwnedDevice(activeAdmin);
             }
@@ -9934,6 +9942,7 @@
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
+        final CallerIdentity caller = getCallerIdentity(who);
         synchronized (getLockObject()) {
             /*
              * When called on the parent DPM instance (parent == true), affects active admin
@@ -9941,9 +9950,14 @@
              * * The ActiveAdmin must be of an org-owned profile owner.
              * * The parent ActiveAdmin instance should be used for managing the restriction.
              */
-            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    parent ? DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER
-                            : DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            final ActiveAdmin ap;
+            if (parent) {
+                ap = getParentOfAdminIfRequired(getOrganizationOwnedProfileOwnerLocked(caller),
+                        parent);
+            } else {
+                ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller), parent);
+            }
+
             if (disabled) {
                 ap.accountTypesWithManagementDisabled.add(accountType);
             } else {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 12c69ea..ddd1f75 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,11 @@
 
 package com.android.server.profcollect;
 
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
@@ -34,6 +39,7 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
 
 /**
  * System-server-local proxy into the {@code IProfcollectd} native service.
@@ -41,28 +47,43 @@
 public final class ProfcollectForwardingService extends SystemService {
     public static final String LOG_TAG = "ProfcollectForwardingService";
 
+    private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
+
+    private static final long BG_PROCESS_PERIOD = DEBUG
+            ? TimeUnit.MINUTES.toMillis(1)
+            : TimeUnit.DAYS.toMillis(1);
+
     private IProfCollectd mIProfcollect;
-    private ProfcollectForwardingService mSelfService;
+    private static ProfcollectForwardingService sSelfService;
     private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
 
     public ProfcollectForwardingService(Context context) {
         super(context);
 
-        if (mSelfService != null) {
+        if (sSelfService != null) {
             throw new AssertionError("only one service instance allowed");
         }
-        mSelfService = this;
+        sSelfService = this;
     }
 
     @Override
     public void onStart() {
-        Log.i(LOG_TAG, "Profcollect forwarding service start");
-        connectNativeService();
-        if (mIProfcollect == null) {
-            return;
+        if (DEBUG) {
+            Log.d(LOG_TAG, "Profcollect forwarding service start");
         }
-        if (serviceHasSupportedTraceProvider()) {
-            registerObservers();
+        connectNativeService();
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_BOOT_COMPLETED) {
+            if (mIProfcollect == null) {
+                return;
+            }
+            if (serviceHasSupportedTraceProvider()) {
+                registerObservers();
+            }
+            ProfcollectBGJobService.schedule(getContext());
         }
     }
 
@@ -130,6 +151,50 @@
         }
     }
 
+    /**
+     * Background trace process service.
+     */
+    public static class ProfcollectBGJobService extends JobService {
+        // Unique ID in system service
+        private static final int JOB_IDLE_PROCESS = 260817;
+        private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
+                "android",
+                ProfcollectBGJobService.class.getName());
+
+        /**
+         * Attach the service to the system job scheduler.
+         */
+        public static void schedule(Context context) {
+            JobScheduler js = context.getSystemService(JobScheduler.class);
+
+            js.schedule(new JobInfo.Builder(JOB_IDLE_PROCESS, JOB_SERVICE_NAME)
+                    .setRequiresDeviceIdle(true)
+                    .setRequiresCharging(true)
+                    .setPeriodic(BG_PROCESS_PERIOD)
+                    .build());
+        }
+
+        @Override
+        public boolean onStartJob(JobParameters params) {
+            if (DEBUG) {
+                Log.d(LOG_TAG, "Starting background process job");
+            }
+
+            try {
+                sSelfService.mIProfcollect.ProcessProfile();
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, e.getMessage());
+            }
+            return true;
+        }
+
+        @Override
+        public boolean onStopJob(JobParameters params) {
+            // TODO: Handle this?
+            return false;
+        }
+    }
+
     // Event observers
     private void registerObservers() {
         registerAppLaunchObserver();
@@ -155,7 +220,9 @@
         int randomNum = ThreadLocalRandom.current().nextInt(100);
         if (randomNum < traceFrequency) {
             try {
-                Log.i(LOG_TAG, "Tracing on app launch event: " + packageName);
+                if (DEBUG) {
+                    Log.d(LOG_TAG, "Tracing on app launch event: " + packageName);
+                }
                 mIProfcollect.TraceOnce("applaunch");
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, e.getMessage());
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 4f636ef..a941331 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -21,12 +21,15 @@
         "truth-prebuilt",
     ],
     static_libs: [
+        "cts-host-utils",
         "frameworks-base-hostutils",
         "PackageManagerServiceHostTestsIntentVerifyUtils",
     ],
     test_suites: ["general-tests"],
     java_resources: [
+        ":PackageManagerTestAppDeclaresStaticLibrary",
         ":PackageManagerTestAppStub",
+        ":PackageManagerTestAppUsesStaticLibrary",
         ":PackageManagerTestAppVersion1",
         ":PackageManagerTestAppVersion2",
         ":PackageManagerTestAppVersion3",
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 24c714c..9399030 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -18,6 +18,7 @@
 
 import com.android.internal.util.test.SystemPreparer
 import com.android.tradefed.device.ITestDevice
+import com.google.common.truth.Truth
 import org.junit.rules.TemporaryFolder
 import java.io.File
 import java.io.FileOutputStream
@@ -48,6 +49,44 @@
 internal fun ITestDevice.uninstallPackages(vararg pkgNames: String) =
         pkgNames.forEach { uninstallPackage(it) }
 
+/**
+ * Retry [block] a total of [maxAttempts] times, waiting [millisBetweenAttempts] milliseconds
+ * between each iteration, until a non-null result is returned, providing that result back to the
+ * caller.
+ *
+ * If an [AssertionError] is thrown by the [block] and a non-null result is never returned, that
+ * error will be re-thrown. This allows the use of [Truth.assertThat] to indicate success while
+ * providing a meaningful error message in case of failure.
+ */
+internal fun <T> retryUntilNonNull(
+    maxAttempts: Int = 10,
+    millisBetweenAttempts: Long = 1000,
+    block: () -> T?
+): T {
+    var attempt = 0
+    var failure: AssertionError? = null
+    while (attempt++ < maxAttempts) {
+        val result = try {
+            block()
+        } catch (e: AssertionError) {
+            failure = e
+            null
+        }
+
+        if (result != null) {
+            return result
+        } else {
+            Thread.sleep(millisBetweenAttempts)
+        }
+    }
+
+    throw failure ?: AssertionError("Never succeeded")
+}
+
+internal fun retryUntilSuccess(block: () -> Boolean) {
+    retryUntilNonNull { block().takeIf { it } }
+}
+
 internal object HostUtils {
 
     fun getDataDir(device: ITestDevice, pkgName: String) =
@@ -78,6 +117,25 @@
      * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot,
      * so the following methods parse the package dump directly to see if the path matches.
      */
+
+    /**
+     * Reads the pm dump for a package name starting from the Packages: metadata section until
+     * the following section.
+     */
+    fun packageSection(
+        device: ITestDevice,
+        pkgName: String,
+        sectionName: String = "Packages"
+    ) = device.executeShellCommand("pm dump $pkgName")
+            .lineSequence()
+            .dropWhile { !it.startsWith(sectionName) } // Wait until the header
+            .drop(1) // Drop the header itself
+            .takeWhile {
+                // Until next top level header, a non-empty line that doesn't start with whitespace
+                it.isEmpty() || it.first().isWhitespace()
+            }
+            .map(String::trim)
+
     fun getCodePaths(device: ITestDevice, pkgName: String) =
             device.executeShellCommand("pm dump $pkgName")
                     .lineSequence()
@@ -87,14 +145,7 @@
                     .toList()
 
     private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
-            device.executeShellCommand("pm dump $pkgName")
-                    .lineSequence()
-                    .dropWhile { !it.startsWith("Packages:") }
-                    .takeWhile {
-                        !it.startsWith("Hidden system packages:") &&
-                                !it.startsWith("Queries:")
-                    }
-                    .map(String::trim)
+            packageSection(device, pkgName)
                     .filter { it.startsWith("User ") }
 
     fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) =
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt
new file mode 100644
index 0000000..9f9e6a3
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SdCardEjectionTests.kt
@@ -0,0 +1,255 @@
+/*
+ * 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.server.pm.test
+
+import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters
+import android.cts.host.utils.DeviceJUnit4Parameterized
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.io.File
+import java.util.regex.Pattern
+
+/**
+ * Verifies PackageManagerService behavior when an app is moved to an adoptable storage device.
+ *
+ * Also has the effect of verifying system behavior when the PackageSetting for a package has no
+ * corresponding AndroidPackage which can be parsed from the APK on disk. This is done by removing
+ * the storage device and causing a reboot, at which point PMS will read PackageSettings from disk
+ * and fail to find the package path.
+ */
+@RunWith(DeviceJUnit4Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(
+        DeviceJUnit4ClassRunnerWithParameters.RunnerFactory::class)
+class SdCardEjectionTests : BaseHostJUnit4Test() {
+
+    companion object {
+        private const val VERSION_DECLARES = "PackageManagerTestAppDeclaresStaticLibrary.apk"
+        private const val VERSION_DECLARES_PKG_NAME =
+                "com.android.server.pm.test.test_app_declares_static_library"
+        private const val VERSION_USES = "PackageManagerTestAppUsesStaticLibrary.apk"
+        private const val VERSION_USES_PKG_NAME =
+                "com.android.server.pm.test.test_app_uses_static_library"
+
+        // TODO(chiuwinson): Use the HostUtils constants when merged
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+
+        @Parameterized.Parameters(name = "reboot={0}")
+        @JvmStatic
+        fun parameters() = arrayOf(false, true)
+
+        data class Volume(
+            val diskId: String,
+            val fsUuid: String
+        )
+    }
+
+    @Rule
+    @JvmField
+    val tempFolder = TemporaryFolder()
+
+    @Parameterized.Parameter(0)
+    @JvmField
+    var reboot: Boolean = false
+
+    @Before
+    @After
+    fun removePackagesAndDeleteVirtualDisk() {
+        device.uninstallPackages(VERSION_ONE, VERSION_USES_PKG_NAME, VERSION_DECLARES_PKG_NAME)
+        removeVirtualDisk()
+        device.reboot()
+    }
+
+    @Test
+    fun launchActivity() {
+        val hostApkFile = HostUtils.copyResourceToHostFile(VERSION_ONE, tempFolder.newFile())
+        assertThat(device.installPackage(hostApkFile, true)).isNull()
+
+        val errorRegex = Pattern.compile("error", Pattern.CASE_INSENSITIVE)
+        fun assertStartResponse(launched: Boolean) {
+            val response = device.executeShellCommand("am start -n $TEST_PKG_NAME/.TestActivity")
+            if (launched) {
+                assertThat(response).doesNotContainMatch(errorRegex)
+            } else {
+                assertThat(response).containsMatch(errorRegex)
+            }
+        }
+
+        assertStartResponse(launched = true)
+
+        val volume = initializeVirtualDisk()
+
+        movePackage(TEST_PKG_NAME, volume)
+        assertStartResponse(launched = true)
+
+        unmount(volume, TEST_PKG_NAME)
+        assertStartResponse(launched = false)
+
+        remount(volume, hostApkFile, TEST_PKG_NAME)
+        assertStartResponse(launched = true)
+    }
+
+    @Test
+    fun uninstallStaticLibraryInUse() {
+        assertThat(device.installJavaResourceApk(tempFolder, VERSION_DECLARES)).isNull()
+
+        val usesApkFile = HostUtils.copyResourceToHostFile(VERSION_USES, tempFolder.newFile())
+        assertThat(device.installPackage(usesApkFile, true)).isNull()
+
+        fun assertUninstallFails() = assertThat(device.uninstallPackage(VERSION_DECLARES_PKG_NAME))
+                .isEqualTo("DELETE_FAILED_USED_SHARED_LIBRARY")
+
+        assertUninstallFails()
+
+        val volume = initializeVirtualDisk()
+
+        movePackage(VERSION_USES_PKG_NAME, volume)
+        assertUninstallFails()
+
+        unmount(volume, VERSION_USES_PKG_NAME)
+        assertUninstallFails()
+
+        remount(volume, usesApkFile, VERSION_USES_PKG_NAME)
+        assertUninstallFails()
+
+        // Check that install in the correct order (uses first) passes
+        assertThat(device.uninstallPackage(VERSION_USES_PKG_NAME)).isNull()
+        assertThat(device.uninstallPackage(VERSION_DECLARES_PKG_NAME)).isNull()
+    }
+
+    private fun initializeVirtualDisk(): Volume {
+        // Rather than making any assumption about what disks/volumes exist on the device,
+        // save the existing disks/volumes to compare and see when a new one pops up, assuming
+        // it was created as the result of the calls in this test.
+        val existingDisks = device.executeShellCommand("sm list-disks adoptable").lines()
+        val existingVolumes = device.executeShellCommand("sm list-volumes private").lines()
+        device.executeShellCommand("sm set-virtual-disk true")
+
+        val diskId = retryUntilNonNull {
+            device.executeShellCommand("sm list-disks adoptable")
+                    .lines()
+                    .filterNot(existingDisks::contains)
+                    .filterNot(String::isEmpty)
+                    .firstOrNull()
+        }
+
+        device.executeShellCommand("sm partition $diskId private")
+
+        return retrieveNewVolume(existingVolumes)
+    }
+
+    private fun retrieveNewVolume(existingVolumes: List<String>): Volume {
+        val newVolume = retryUntilNonNull {
+            device.executeShellCommand("sm list-volumes private")
+                    .lines()
+                    .toMutableList()
+                    .apply { removeAll(existingVolumes) }
+                    .firstOrNull()
+                    ?.takeIf { it.isNotEmpty() }
+        }
+
+        val sections = newVolume.split(" ")
+        return Volume(diskId = sections.first(), fsUuid = sections.last()).also {
+            assertThat(it.diskId).isNotEmpty()
+            assertThat(it.fsUuid).isNotEmpty()
+        }
+    }
+
+    private fun removeVirtualDisk() {
+        device.executeShellCommand("sm set-virtual-disk false")
+        retryUntilSuccess {
+            !device.executeShellCommand("sm list-volumes").contains("ejecting")
+        }
+    }
+
+    private fun movePackage(pkgName: String, volume: Volume) {
+        // TODO(b/167241596): oat dir must exist for a move install
+        val codePath = HostUtils.getCodePaths(device, pkgName).first()
+        device.executeShellCommand("mkdir $codePath/oat")
+        assertThat(device.executeShellCommand(
+                "pm move-package $pkgName ${volume.fsUuid}").trim())
+                .isEqualTo("Success")
+    }
+
+    private fun unmount(volume: Volume, pkgName: String) {
+        assertThat(device.executeShellCommand("sm unmount ${volume.diskId}")).isEmpty()
+        if (reboot) {
+            // The system automatically mounts the virtual disk on startup, which would mean the
+            // app files are available to the system. To prevent this, disable the disk entirely.
+            // TODO: There must be a better way to prevent it from auto-mounting.
+            removeVirtualDisk()
+            device.reboot()
+        } else {
+            // Because PackageManager unmount scan is asynchronous, need to retry until the package
+            // has been unloaded. This only has to be done in the non-reboot case. Reboot will
+            // clear the data structure by its nature.
+            retryUntilSuccess {
+                // The compiler section will print the state of the physical APK
+                HostUtils.packageSection(device, pkgName, sectionName = "Compiler stats")
+                        .any { it.contains("Unable to find package: $pkgName") }
+            }
+        }
+    }
+
+    private fun remount(volume: Volume, hostApkFile: File, pkgName: String) {
+        if (reboot) {
+            // Because the disk was destroyed when unmounting, it now has to be rebuilt manually.
+            // This enables a new virtual disk, unmounts it, mutates its UUID to match the previous
+            // partition's, remounts it, and pushes the base.apk back onto the device. This
+            // simulates the same disk being re-inserted. This is very hacky.
+            val newVolume = initializeVirtualDisk()
+            val mountPoint = device.executeShellCommand("mount")
+                    .lineSequence()
+                    .first { it.contains(newVolume.fsUuid) }
+                    .takeWhile { !it.isWhitespace() }
+
+            device.executeShellCommand("sm unmount ${newVolume.diskId}")
+
+            // Save without renamed UUID to compare and see when the renamed pops up
+            val existingVolumes = device.executeShellCommand("sm list-volumes private").lines()
+
+            device.executeShellCommand("make_f2fs -U ${volume.fsUuid} $mountPoint")
+            device.executeShellCommand("sm mount ${newVolume.diskId}")
+
+            val reparsedVolume = retrieveNewVolume(existingVolumes)
+            assertThat(reparsedVolume.fsUuid).isEqualTo(volume.fsUuid)
+
+            val codePath = HostUtils.getCodePaths(device, pkgName).first()
+            device.pushFile(hostApkFile, "$codePath/base.apk")
+
+            // Unmount so following remount will re-kick package scan
+            device.executeShellCommand("sm unmount ${newVolume.diskId}")
+        }
+
+        device.executeShellCommand("sm mount ${volume.diskId}")
+
+        // Because PackageManager remount scan is asynchronous, need to retry until the package
+        // has been loaded and added to the internal structures. Otherwise resolution will fail.
+        retryUntilSuccess {
+            // The compiler section will print the state of the physical APK
+            HostUtils.packageSection(device, pkgName, sectionName = "Compiler stats")
+                    .none { it.contains("Unable to find package: $pkgName") }
+        }
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
similarity index 100%
rename from services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
rename to services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestOriginalOverride.xml
similarity index 100%
rename from services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
rename to services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestOriginalOverride.xml
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion1.xml
similarity index 84%
rename from services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
rename to services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion1.xml
index efc7372..05b6248 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion1.xml
@@ -25,4 +25,9 @@
         android:protectionLevel="normal"
         />
 
+    <application>
+        <activity android:name="com.android.server.pm.test.test_app.TestActivity"
+            android:label="PackageManagerTestApp" />
+    </application>
+
 </manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion2.xml
similarity index 97%
rename from services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
rename to services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion2.xml
index 620054c..d6eb3e0 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion2.xml
@@ -25,4 +25,6 @@
         android:protectionLevel="normal"
         />
 
+    <application/>
+
 </manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion3.xml
similarity index 97%
rename from services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
rename to services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion3.xml
index 1997771..90317cb 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion3.xml
@@ -25,4 +25,6 @@
         android:protectionLevel="normal"
         />
 
+    <application/>
+
 </manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion4.xml
similarity index 97%
rename from services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
rename to services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion4.xml
index d6ade03..795c3c1 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/AndroidManifestVersion4.xml
@@ -25,4 +25,6 @@
         android:protectionLevel="normal"
         />
 
+    <application/>
+
 </manifest>
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/src/com/android/server/pm/test/test_app/TestActivity.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
copy to services/tests/PackageManagerServiceTests/host/test-apps/Generic/src/com/android/server/pm/test/test_app/TestActivity.kt
index e65f19d..0c9b8d4 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/src/com/android/server/pm/test/test_app/TestActivity.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,17 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.keyguard.dagger;
+package com.android.server.pm.test.test_app
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.app.Activity
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface ContainerView {
-}
+class TestActivity : Activity()
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
new file mode 100644
index 0000000..58f17f2
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
@@ -0,0 +1,24 @@
+// 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.
+
+android_test_helper_app {
+    name: "PackageManagerTestAppDeclaresStaticLibrary",
+    manifest: "AndroidManifestDeclaresStaticLibrary.xml",
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+}
+
+android_test_helper_app {
+    name: "PackageManagerTestAppUsesStaticLibrary",
+    manifest: "AndroidManifestUsesStaticLibrary.xml",
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/AndroidManifestDeclaresStaticLibrary.xml
similarity index 64%
copy from services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
copy to services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/AndroidManifestDeclaresStaticLibrary.xml
index 1997771..8a6c01a 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/AndroidManifestDeclaresStaticLibrary.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   ~ Copyright (C) 2020 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,15 +13,12 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.test_app"
-    android:versionCode="3"
-    >
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.pm.test.test_app_declares_static_library">
 
-    <permission
-        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
-        android:protectionLevel="normal"
-        />
+    <application>
+        <static-library android:name="com.android.server.pm.test.static_library"
+            android:version="1" />
+    </application>
 
 </manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/AndroidManifestUsesStaticLibrary.xml b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/AndroidManifestUsesStaticLibrary.xml
new file mode 100644
index 0000000..82d9ac4
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/AndroidManifestUsesStaticLibrary.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.pm.test.test_app_uses_static_library">
+
+    <application>
+        <activity android:name="com.android.server.pm.test.static_library.TestActivity"
+            android:label="TestActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <uses-static-library android:name="com.android.server.pm.test.static_library"
+            android:certDigest="1F:BE:5F:FB:B0:AD:DC:0C:CD:BF:22:B9:8A:2F:5A:58:A5:C8:29:80:E1:30:2F:65:0E:6B:CA:ED:03:82:BF:CF"
+            android:version="1" />
+    </application>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/src/com/android/server/pm/test/static_library/TestActivity.kt b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/src/com/android/server/pm/test/static_library/TestActivity.kt
new file mode 100644
index 0000000..fa85258
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/src/com/android/server/pm/test/static_library/TestActivity.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.server.pm.test.static_library
+
+import android.app.Activity
+import android.graphics.Color
+import android.os.Bundle
+import android.view.ViewGroup
+import android.widget.FrameLayout
+
+class TestActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(FrameLayout(this).apply {
+            setBackgroundColor(Color.BLUE)
+        }, ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT
+        ))
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 82726c7..5d8f662 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -173,6 +173,8 @@
                 mock(OomAdjProfiler.class));
         setFieldValue(ActivityManagerService.class, sService, "mUserController",
                 mock(UserController.class));
+        setFieldValue(ActivityManagerService.class, sService, "mAppProfiler",
+                mock(AppProfiler.class));
         doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
                 .enqueueProcessChangeItemLocked(anyInt(), anyInt());
         sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index c8baca6..8062bfe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -170,6 +170,30 @@
         verify(mMockSystemSupport).onClientChangeLocked(false);
     }
 
+    @Test
+    public void uiAutomationWithDontUseAccessibilityFlagAfterUnregistering_notifiesSystem()
+            throws Exception {
+        register(UiAutomation.FLAG_DONT_USE_ACCESSIBILITY);
+        unregister();
+        verify(mMockSystemSupport).onClientChangeLocked(false);
+    }
+
+    @Test
+    public void uiAutomationWithDontUseAccessibilityFlag_disableAccessibilityFunctions()
+            throws Exception {
+        register(0);
+        assertTrue(mUiAutomationManager.isUiAutomationRunningLocked());
+        unregister();
+        assertFalse(mUiAutomationManager.isUiAutomationRunningLocked());
+        register(UiAutomation.FLAG_DONT_USE_ACCESSIBILITY);
+        assertTrue(mUiAutomationManager.isUiAutomationRunningLocked());
+        assertFalse(mUiAutomationManager.useAccessibility());
+        assertFalse(mUiAutomationManager.canRetrieveInteractiveWindowsLocked());
+        assertFalse(mUiAutomationManager.isTouchExplorationEnabledLocked());
+        assertEquals(0, mUiAutomationManager.getRelevantEventTypes());
+        assertEquals(0, mUiAutomationManager.getRequestedEventMaskLocked());
+    }
+
     private void register(int flags) {
         mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
                 mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 1d04c83..e02a46af 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -984,8 +984,8 @@
         }
 
         @Override
-        public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
-            ActivityManager.StackInfo focusedStack = new ActivityManager.StackInfo();
+        public RootTaskInfo getFocusedStack() throws RemoteException {
+            RootTaskInfo focusedStack = new RootTaskInfo();
             focusedStack.userId = 0;
             focusedStack.topActivity = new ComponentName("a.package", "a.class");
             return focusedStack;
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 0839273..154d42c 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -21,172 +21,387 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageInfo
 import android.os.Process
-import org.junit.Rule
+import com.android.server.om.OverlayActorEnforcer.ActorState
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.BeforeClass
 import org.junit.Test
-import org.junit.rules.ExpectedException
-import java.lang.UnsupportedOperationException
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.spy
+import java.io.IOException
 
+@RunWith(Parameterized::class)
 class OverlayActorEnforcerTests {
+
     companion object {
-        private const val NAMESPACE = "testnamespace"
-        private const val ACTOR_NAME = "testactor"
-        private const val ACTOR_PKG_NAME = "com.test.actor.one"
+        private const val TARGET_PKG = "com.test.target"
+        private const val OVERLAY_PKG = "com.test.overlay"
+
+        private const val VALID_NAMESPACE = "testNamespaceValid"
+        private const val INVALID_NAMESPACE = "testNamespaceInvalid"
+        private const val VALID_ACTOR_NAME = "testActorOne"
+        private const val INVALID_ACTOR_NAME = "testActorTwo"
+        private const val VALID_ACTOR_PKG = "com.test.actor.valid"
+        private const val INVALID_ACTOR_PKG = "com.test.actor.invalid"
         private const val OVERLAYABLE_NAME = "TestOverlayable"
-        private const val UID = 3536
+        private const val NULL_UID = 3536
+        private const val EMPTY_UID = NULL_UID + 1
+        private const val INVALID_ACTOR_UID = NULL_UID + 2
+        private const val VALID_ACTOR_UID = NULL_UID + 3
+        private const val TARGET_UID = NULL_UID + 4
         private const val USER_ID = 55
-    }
 
-    @get:Rule
-    val expectedException = ExpectedException.none()!!
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun parameters() = CASES.mapIndexed { caseIndex, testCase ->
+            fun param(pair: Pair<String, TestState.() -> Unit>, type: Params.Type): Params {
+                val expectedState = testCase.state.takeUnless { type == Params.Type.ALLOWED }
+                        ?: ActorState.ALLOWED
+                val (caseName, case) = pair
+                val testName = makeTestName(testCase, caseName, type)
+                return Params(caseIndex, expectedState, testName, type, case)
+            }
 
-    @Test
-    fun isRoot() {
-        verify(callingUid = Process.ROOT_UID)
-    }
+            testCase.failures.map { param(it, Params.Type.FAILURE) } +
+                    testCase.allowed.map { param(it, Params.Type.ALLOWED) }
+        }.flatten()
 
-    @Test(expected = SecurityException::class)
-    fun isShell() {
-        verify(callingUid = Process.SHELL_UID)
-    }
-
-    @Test
-    fun isSystem() {
-        verify(callingUid = Process.SYSTEM_UID)
-    }
-
-    @Test(expected = SecurityException::class)
-    fun noOverlayable_noTarget() {
-        verify(targetOverlayableName = null)
-    }
-
-    @Test
-    fun noOverlayable_noTarget_withPermission() {
-        verify(targetOverlayableName = null, hasPermission = true)
-    }
-
-    @Test(expected = SecurityException::class)
-    fun noOverlayable_withTarget() {
-        verify(targetOverlayableName = OVERLAYABLE_NAME)
-    }
-
-    @Test(expected = SecurityException::class)
-    fun withOverlayable_noTarget() {
-        verify(
-                targetOverlayableName = null,
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null)
-        )
-    }
-
-    @Test(expected = SecurityException::class)
-    fun withOverlayable_noActor() {
-        verify(
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null)
-        )
-    }
-
-    @Test
-    fun withOverlayable_noActor_withPermission() {
-        verify(
-                hasPermission = true,
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null)
-        )
-    }
-
-    @Test(expected = SecurityException::class)
-    fun withOverlayable_withActor_notActor() {
-        verify(
-                isActor = false,
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME,
-                        "overlay://$NAMESPACE/$ACTOR_NAME")
-        )
-    }
-
-    @Test(expected = SecurityException::class)
-    fun withOverlayable_withActor_isActor_notPreInstalled() {
-        verify(
-                isActor = true,
-                isPreInstalled = false,
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME,
-                        "overlay://$NAMESPACE/$ACTOR_NAME")
-        )
-    }
-
-    @Test
-    fun withOverlayable_withActor_isActor_isPreInstalled() {
-        verify(
-                isActor = true,
-                isPreInstalled = true,
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME,
-                        "overlay://$NAMESPACE/$ACTOR_NAME")
-        )
-    }
-
-    @Test(expected = SecurityException::class)
-    fun withOverlayable_invalidActor() {
-        verify(
-                isActor = true,
-                isPreInstalled = true,
-                overlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, "notValidActor")
-        )
-    }
-
-    private fun verify(
-        isActor: Boolean = false,
-        isPreInstalled: Boolean = false,
-        hasPermission: Boolean = false,
-        overlayableInfo: OverlayableInfo? = null,
-        callingUid: Int = UID,
-        targetOverlayableName: String? = OVERLAYABLE_NAME
-    ) {
-        val callback = MockCallback(
-                isActor = isActor,
-                isPreInstalled = isPreInstalled,
-                hasPermission = hasPermission,
-                overlayableInfo = overlayableInfo
-        )
-
-        val overlayInfo = overlayInfo(targetOverlayableName)
-        OverlayActorEnforcer(callback)
-                .enforceActor(overlayInfo, "test", callingUid, USER_ID)
-    }
-
-    private fun overlayInfo(targetOverlayableName: String?) = OverlayInfo("com.test.overlay",
-            "com.test.target", targetOverlayableName, null, "/path", OverlayInfo.STATE_UNKNOWN, 0,
-            0, false)
-
-    private class MockCallback(
-        private val isActor: Boolean = false,
-        private val isPreInstalled: Boolean = false,
-        private val hasPermission: Boolean = false,
-        private val overlayableInfo: OverlayableInfo? = null,
-        private vararg val packageNames: String = arrayOf("com.test.actor.one")
-    ) : PackageManagerHelper {
-
-        override fun getNamedActors() = if (isActor) {
-            mapOf(NAMESPACE to mapOf(ACTOR_NAME to ACTOR_PKG_NAME))
-        } else {
-            emptyMap()
+        @BeforeClass
+        @JvmStatic
+        fun checkAllCasesHandled() {
+            // Assert that all states have been tested at least once.
+            assertThat(CASES.map { it.state }.distinct()).containsAllIn(ActorState.values())
         }
 
+        @BeforeClass
+        @JvmStatic
+        fun checkAllCasesUniquelyNamed() {
+            val duplicateCaseNames = CASES.mapIndexed { caseIndex, testCase ->
+                testCase.failures.map {
+                    makeTestName(testCase, it.first, Params.Type.FAILURE)
+                } + testCase.allowed.map {
+                    makeTestName(testCase, it.first, Params.Type.ALLOWED)
+                }
+            }
+                    .flatten()
+                    .groupingBy { it }
+                    .eachCount()
+                    .filterValues { it > 1 }
+                    .keys
+
+            assertThat(duplicateCaseNames).isEmpty()
+        }
+
+        /*
+            The pattern in this block is a result of the incredible number of branches in
+            enforcement logic. It serves to verify failures with the assumption that all errors
+            are checked in order. The idea is to emulate the if-else branches from code, but using
+            actual test data instead of if statements.
+
+            Each state is verified by providing a failure or exclusive set of failures which cause
+            a failure state to be returned. Each state also provides a success case which will
+            "skip" the state. This allows subsequent failure cases to cascade from the first case
+            by calling all the skip branches for preceding states and then choosing only 1 of
+            the failures to test.
+
+            Given the failure states A, B, and C: testA calls A.failure + assert, testB calls
+            A.skip + B.failure + assert, testC calls A.skip + B.skip + C.failure + assert, etc.
+
+            Calling `allowed` is a special case for when there is a combination of parameters that
+            skips the remaining checks and immediately allows the actor through. For these cases,
+            the first failure branch will be run, assert that it's not allowed, and the
+            allowed branch will run, asserting that it now results in ALLOWED, skipping all
+            remaining functions.
+
+            This is an ordered list of TestCase objects, with the possibility to repeat failure
+            states if any can occur multiple times in the logic tree.
+
+            Each failure must be handled at least once.
+         */
+        private val CASES = listOf(
+                ActorState.TARGET_NOT_FOUND withCases {
+                    failure("nullPkgInfo") { targetPkgInfo = null }
+                    allowed("debuggable") {
+                        targetPkgInfo = pkgInfo(TARGET_PKG).apply {
+                            applicationInfo.flags = ApplicationInfo.FLAG_DEBUGGABLE
+                        }
+                    }
+                    skip { targetPkgInfo = pkgInfo(TARGET_PKG) }
+                },
+                ActorState.NO_PACKAGES_FOR_UID withCases {
+                    failure("empty") { callingUid = EMPTY_UID }
+                    failure("null") { callingUid = NULL_UID }
+                    failure("shell") { callingUid = Process.SHELL_UID }
+                    allowed("targetUid") { callingUid = TARGET_UID }
+                    allowed("rootUid") { callingUid = Process.ROOT_UID }
+                    allowed("systemUid") { callingUid = Process.SYSTEM_UID }
+                    skip { callingUid = INVALID_ACTOR_UID }
+                },
+                ActorState.MISSING_TARGET_OVERLAYABLE_NAME withCases {
+                    failure("nullTargetOverlayableName") {
+                        overlayInfoParams.targetOverlayableName = null
+                        targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME,
+                                "overlay://$VALID_NAMESPACE/$VALID_ACTOR_NAME")
+                    }
+                    skip { overlayInfoParams.targetOverlayableName = OVERLAYABLE_NAME }
+                },
+                ActorState.MISSING_LEGACY_PERMISSION withCases {
+                    failure("noPermission") {
+                        overlayInfoParams.targetOverlayableName = null
+                        targetOverlayableInfo = null
+                        hasPermission = false
+                    }
+                    allowed("hasPermission") { hasPermission = true }
+                    skip { overlayInfoParams.targetOverlayableName = OVERLAYABLE_NAME }
+                },
+                ActorState.ERROR_READING_OVERLAYABLE withCases {
+                    failure("doesTargetDefineOverlayableIOException") {
+                        overlayInfoParams.targetOverlayableName = null
+                        whenever(doesTargetDefineOverlayable(TARGET_PKG, USER_ID))
+                                .thenThrow(IOException::class.java)
+                    }
+                    skip { overlayInfoParams.targetOverlayableName = OVERLAYABLE_NAME }
+                },
+                ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE withCases {
+                    failure("getOverlayableForTargetIOException") {
+                        whenever(getOverlayableForTarget(TARGET_PKG, OVERLAYABLE_NAME,
+                                USER_ID)).thenThrow(IOException::class.java)
+                    }
+                },
+                ActorState.MISSING_OVERLAYABLE withCases {
+                    failure("nullTargetOverlayableInfo") { targetOverlayableInfo = null }
+                    skip {
+                        targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME,
+                                "overlay://$VALID_NAMESPACE/$VALID_ACTOR_NAME")
+                    }
+                },
+                ActorState.MISSING_LEGACY_PERMISSION withCases {
+                    failure("noPermissionNullActor") {
+                        targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, null)
+                        hasPermission = false
+                    }
+                    failure("noPermissionEmptyActor") {
+                        targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, "")
+                        hasPermission = false
+                    }
+                    allowed("hasPermissionNullActor") {
+                        hasPermission = true
+                    }
+                    skip {
+                        targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME,
+                                "overlay://$VALID_NAMESPACE/$VALID_ACTOR_NAME")
+                    }
+                },
+                ActorState.INVALID_OVERLAYABLE_ACTOR_NAME withCases {
+                    fun TestState.mockActor(actorUri: String) {
+                        targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, actorUri)
+                    }
+                    failure("wrongScheme") {
+                        mockActor("notoverlay://$VALID_NAMESPACE/$VALID_ACTOR_NAME")
+                    }
+                    failure("extraPath") {
+                        mockActor("overlay://$VALID_NAMESPACE/$VALID_ACTOR_NAME/extraPath")
+                    }
+                    failure("missingPath") { mockActor("overlay://$VALID_NAMESPACE") }
+                    failure("missingAuthority") { mockActor("overlay://") }
+                    skip { mockActor("overlay://$VALID_NAMESPACE/$VALID_ACTOR_NAME") }
+                },
+                ActorState.NO_NAMED_ACTORS withCases {
+                    failure("empty") { namedActorsMap = emptyMap() }
+                    skip {
+                        namedActorsMap = mapOf(INVALID_NAMESPACE to
+                                mapOf(INVALID_ACTOR_NAME to VALID_ACTOR_PKG))
+                    }
+                },
+                ActorState.MISSING_NAMESPACE withCases {
+                    failure("invalidNamespace") {
+                        namedActorsMap = mapOf(INVALID_NAMESPACE to
+                                mapOf(INVALID_ACTOR_NAME to VALID_ACTOR_PKG))
+                    }
+                    skip {
+                        namedActorsMap = mapOf(VALID_NAMESPACE to
+                                mapOf(INVALID_ACTOR_NAME to VALID_ACTOR_PKG))
+                    }
+                },
+                ActorState.MISSING_ACTOR_NAME withCases {
+                    failure("invalidActorName") {
+                        namedActorsMap = mapOf(VALID_NAMESPACE to
+                                mapOf(INVALID_ACTOR_NAME to VALID_ACTOR_PKG))
+                    }
+                    skip {
+                        namedActorsMap = mapOf(VALID_NAMESPACE to
+                                mapOf(VALID_ACTOR_NAME to VALID_ACTOR_PKG))
+                    }
+                },
+                ActorState.MISSING_APP_INFO withCases {
+                    failure("nullActorPkgInfo") { actorPkgInfo = null }
+                    failure("nullActorAppInfo") {
+                        actorPkgInfo = PackageInfo().apply { applicationInfo = null }
+                    }
+                    skip { actorPkgInfo = pkgInfo(VALID_ACTOR_PKG) }
+                },
+                ActorState.ACTOR_NOT_PREINSTALLED withCases {
+                    failure("notSystem") {
+                        actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply {
+                            applicationInfo.flags = 0
+                        }
+                    }
+                    skip {
+                        actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply {
+                            applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM
+                        }
+                    }
+                },
+                ActorState.INVALID_ACTOR withCases {
+                    failure("invalidUid") { callingUid = INVALID_ACTOR_UID }
+                    skip { callingUid = VALID_ACTOR_UID }
+                },
+                ActorState.ALLOWED withCases {
+                    // No point making an exception for this case in all of the test code, so
+                    // just pretend this is a failure that results in a success result code.
+                    failure("allowed") { /* Do nothing */ }
+                }
+        )
+
+        data class OverlayInfoParams(
+            var targetPackageName: String = TARGET_PKG,
+            var targetOverlayableName: String? = null
+        ) {
+            fun toOverlayInfo() = OverlayInfo(
+                    OVERLAY_PKG,
+                    targetPackageName,
+                    targetOverlayableName,
+                    null,
+                    "/path",
+                    OverlayInfo.STATE_UNKNOWN, 0,
+                    0, false)
+        }
+
+        private infix fun ActorState.withCases(block: TestCase.() -> Unit) =
+                TestCase(this).apply(block)
+
+        private fun pkgInfo(pkgName: String): PackageInfo = mockThrowOnUnmocked {
+            this.packageName = pkgName
+            this.applicationInfo = ApplicationInfo().apply {
+                this.packageName = pkgName
+            }
+        }
+
+        private fun makeTestName(testCase: TestCase, caseName: String, type: Params.Type): String {
+            val resultSuffix = if (type == Params.Type.ALLOWED) "allowed" else "failed"
+            return "${testCase.state}_${resultSuffix}_$caseName"
+        }
+    }
+
+    @Parameterized.Parameter(0)
+    lateinit var params: Params
+
+    @Test
+    fun verify() {
+        // Apply all the skip states before the failure to be verified
+        val testState = CASES.take(params.index)
+                .fold(TestState.create()) { testState, case ->
+                    testState.apply(case.skip)
+                }
+
+        // If testing an allowed branch, first apply a failure to ensure it fails
+        if (params.type == Params.Type.ALLOWED) {
+            CASES[params.index].failures.firstOrNull()?.second?.run(testState::apply)
+            assertThat(testState.toResult()).isNotEqualTo(ActorState.ALLOWED)
+        }
+
+        // Apply the test case in the params to the collected state
+        testState.apply(params.function)
+
+        // Assert the result matches the expected state
+        assertThat(testState.toResult()).isEqualTo(params.expectedState)
+    }
+
+    private fun TestState.toResult() = OverlayActorEnforcer(this)
+            .isAllowedActor("test", overlayInfoParams.toOverlayInfo(), callingUid, USER_ID)
+
+    data class Params(
+        var index: Int,
+        var expectedState: ActorState,
+        val testName: String,
+        val type: Type,
+        val function: TestState.() -> Unit
+    ) {
+        override fun toString() = testName
+
+        enum class Type {
+            FAILURE,
+            ALLOWED
+        }
+    }
+
+    data class TestCase(
+        val state: ActorState,
+        val failures: MutableList<Pair<String, TestState.() -> Unit>> = mutableListOf(),
+        var allowed: MutableList<Pair<String, TestState.() -> Unit>> = mutableListOf(),
+        var skip: (TestState.() -> Unit) = {}
+    ) {
+        fun failure(caseName: String, block: TestState.() -> Unit) {
+            failures.add(caseName to block)
+        }
+
+        fun allowed(caseName: String, block: TestState.() -> Unit) {
+            allowed.add(caseName to block)
+        }
+
+        fun skip(block: TestState.() -> Unit) {
+            this.skip = block
+        }
+    }
+
+    open class TestState private constructor(
+        var callingUid: Int = NULL_UID,
+        val overlayInfoParams: OverlayInfoParams = OverlayInfoParams(),
+        var namedActorsMap: Map<String, Map<String, String>> = emptyMap(),
+        var hasPermission: Boolean = false,
+        var targetOverlayableInfo: OverlayableInfo? = null,
+        var targetPkgInfo: PackageInfo? = null,
+        var actorPkgInfo: PackageInfo? = null,
+        vararg val packageNames: String = arrayOf("com.test.actor.one")
+    ) : PackageManagerHelper {
+
+        companion object {
+            // Enforce that new instances are spied
+            fun create() = spy(TestState())!!
+        }
+
+        override fun getNamedActors() = namedActorsMap
+
+        @Throws(IOException::class)
         override fun getOverlayableForTarget(
             packageName: String,
             targetOverlayableName: String,
             userId: Int
-        ) = overlayableInfo
+        ) = targetOverlayableInfo?.takeIf {
+            // Protect against this method being called with the wrong package name
+            targetPkgInfo == null || targetPkgInfo?.packageName == packageName
+        }
 
         override fun getPackagesForUid(uid: Int) = when (uid) {
-            UID -> packageNames
+            EMPTY_UID -> emptyArray()
+            INVALID_ACTOR_UID -> arrayOf(INVALID_ACTOR_PKG)
+            VALID_ACTOR_UID -> arrayOf(VALID_ACTOR_PKG)
+            TARGET_UID -> arrayOf(TARGET_PKG)
+            NULL_UID -> null
             else -> null
         }
 
-        override fun getPackageInfo(packageName: String, userId: Int) = PackageInfo().apply {
-            applicationInfo = ApplicationInfo().apply {
-                flags = if (isPreInstalled) ApplicationInfo.FLAG_SYSTEM else 0
-            }
-        }
+        override fun getPackageInfo(packageName: String, userId: Int) =
+                listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName }
 
+        @Throws(IOException::class) // Mockito requires this checked exception to be declared
         override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean {
-            return overlayableInfo != null
+            return targetOverlayableInfo?.takeIf {
+                // Protect against this method being called with the wrong package name
+                targetPkgInfo == null || targetPkgInfo?.packageName == targetPackageName
+            } != null
         }
 
         override fun enforcePermission(permission: String?, message: String?) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 0bf06bb..dac0542 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -45,6 +45,7 @@
 import android.content.pm.UserInfo;
 import android.os.BaseBundle;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.util.ArrayMap;
@@ -59,8 +60,12 @@
 
 import com.android.permission.persistence.RuntimePermissionsPersistence;
 import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.PermissionSettings;
 
+import com.google.common.truth.Truth;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -380,6 +385,74 @@
     }
 
     @Test
+    public void testWriteReadUsesStaticLibraries() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final Object lock = new Object();
+        final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings,
+                mRuntimePermissionsPersistence, lock);
+        final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+        ps1.appId = Process.FIRST_APPLICATION_UID;
+        ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
+                .setUid(ps1.appId)
+                .setSystem(true)
+                .hideAsFinal();
+        final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+        ps2.appId = Process.FIRST_APPLICATION_UID + 1;
+        ps2.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed())
+                .setUid(ps2.appId)
+                .hideAsFinal();
+
+        ps1.usesStaticLibraries = new String[] { "com.example.shared.one" };
+        ps1.usesStaticLibrariesVersions = new long[] { 12 };
+        ps1.setFlags(ps1.pkgFlags | ApplicationInfo.FLAG_SYSTEM);
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+        assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true));
+
+        ps2.usesStaticLibraries = new String[] { "com.example.shared.two" };
+        ps2.usesStaticLibrariesVersions = new long[] { 34 };
+        settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+        settingsUnderTest.writeLPr();
+
+        settingsUnderTest.mPackages.clear();
+        settingsUnderTest.mDisabledSysPackages.clear();
+
+        assertThat(settingsUnderTest.readLPw(createFakeUsers()), is(true));
+
+        PackageSetting readPs1 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_1);
+        PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
+
+        Truth.assertThat(readPs1).isNotNull();
+        Truth.assertThat(readPs1.usesStaticLibraries).isNotNull();
+        Truth.assertThat(readPs1.usesStaticLibrariesVersions).isNotNull();
+        Truth.assertThat(readPs2).isNotNull();
+        Truth.assertThat(readPs2.usesStaticLibraries).isNotNull();
+        Truth.assertThat(readPs2.usesStaticLibrariesVersions).isNotNull();
+
+        List<Long> ps1VersionsAsList = new ArrayList<>();
+        for (long version : ps1.usesStaticLibrariesVersions) {
+            ps1VersionsAsList.add(version);
+        }
+
+        List<Long> ps2VersionsAsList = new ArrayList<>();
+        for (long version : ps2.usesStaticLibrariesVersions) {
+            ps2VersionsAsList.add(version);
+        }
+
+        Truth.assertThat(readPs1.usesStaticLibraries).asList()
+                .containsExactlyElementsIn(ps1.usesStaticLibraries).inOrder();
+
+        Truth.assertThat(readPs1.usesStaticLibrariesVersions).asList()
+                .containsExactlyElementsIn(ps1VersionsAsList).inOrder();
+
+        Truth.assertThat(readPs2.usesStaticLibraries).asList()
+                .containsExactlyElementsIn(ps2.usesStaticLibraries).inOrder();
+
+        Truth.assertThat(readPs2.usesStaticLibrariesVersions).asList()
+                .containsExactlyElementsIn(ps2VersionsAsList).inOrder();
+    }
+
+    @Test
     public void testPackageRestrictionsDistractionFlagsDefault() {
         final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
         assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE));
@@ -617,7 +690,7 @@
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
-        assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
@@ -656,7 +729,7 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(0));
-        assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
@@ -700,7 +773,7 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(10064));
-        assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
@@ -741,7 +814,7 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(10064));
-        assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
@@ -792,10 +865,10 @@
     private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
         assertThat(origPkgSetting, is(not(testPkgSetting)));
         assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
-        assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath());
-        assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath()));
-        assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString());
-        assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString()));
+        assertSame(origPkgSetting.getPath(), testPkgSetting.getPath());
+        assertThat(origPkgSetting.getPath(), is(testPkgSetting.getPath()));
+        assertSame(origPkgSetting.getPathString(), testPkgSetting.getPathString());
+        assertThat(origPkgSetting.getPathString(), is(testPkgSetting.getPathString()));
         assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
         assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
         assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 2651cfa..1d384e9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -312,7 +312,7 @@
 
     private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
         return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
-                new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
+                new File(pkg.getPath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
                 null, pkg.getVersionCode(),
                 PackageInfoUtils.appInfoFlags(pkg, null),
                 PackageInfoUtils.appInfoPrivateFlags(pkg, null),
@@ -335,8 +335,8 @@
         assertEquals(a.getPackageName(), b.getPackageName());
         assertArrayEquals(a.getSplitNames(), b.getSplitNames());
         assertEquals(a.getVolumeUuid(), b.getVolumeUuid());
-        assertEquals(a.getCodePath(), b.getCodePath());
-        assertEquals(a.getBaseCodePath(), b.getBaseCodePath());
+        assertEquals(a.getPath(), b.getPath());
+        assertEquals(a.getBaseApkPath(), b.getBaseApkPath());
         assertArrayEquals(a.getSplitCodePaths(), b.getSplitCodePaths());
         assertArrayEquals(a.getSplitRevisionCodes(), b.getSplitRevisionCodes());
         assertArrayEquals(a.getSplitFlags(), b.getSplitFlags());
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 56dddb0..4d8cc4647 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -533,7 +533,7 @@
                 arrayContaining("some.static.library", "some.other.static.library"));
         assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
         assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
-        assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName))));
+        assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
         assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index caa8ae5..3ce7a7d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -29,12 +29,10 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.parsing.ApkLiteParseUtils;
-import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.FileUtils;
 
-import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -106,9 +104,9 @@
 
         Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
         assertEquals(1, packageDexMetadata.size());
-        String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
+        String baseDexMetadata = packageDexMetadata.get(pkg.getBaseApkPath());
         assertNotNull(baseDexMetadata);
-        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
+        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseApkPath()));
     }
 
     @Test
@@ -122,9 +120,9 @@
 
         Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
         assertEquals(2, packageDexMetadata.size());
-        String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
+        String baseDexMetadata = packageDexMetadata.get(pkg.getBaseApkPath());
         assertNotNull(baseDexMetadata);
-        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
+        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseApkPath()));
 
         String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
         assertNotNull(splitDexMetadata);
diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
index 056fa88..59cbd1c 100644
--- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
+++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
@@ -22,8 +22,6 @@
 import org.mockito.stubbing.Answer
 import org.mockito.stubbing.Stubber
 
-// TODO(chiuwinson): Move this entire file to a shared utility module
-// TODO(b/135203078): De-dupe utils added for overlays vs package refactor
 object MockitoUtils {
     val ANSWER_THROWS = Answer<Any?> {
         when (val name = it.method.name) {
@@ -64,7 +62,7 @@
 
 fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
 
-inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit): T {
+inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}): T {
     val swappingAnswer = object : Answer<Any?> {
         var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
 
@@ -81,4 +79,5 @@
             }
 }
 
-inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit) = spyThrowOnUnmocked<T>(null, block)
+inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) =
+        spyThrowOnUnmocked<T>(null, block)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 8644719..9e7226e7 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1086,12 +1086,18 @@
     }
 
     /**
-     * Confirm the system user on automotive devices can use car categories
+     * Confirm an application with the SEND_CATEGORY_CAR_NOTIFICATIONS permission on automotive
+     * devices can use car categories.
      */
     @Test
-    public void testEnqueuedRestrictedNotifications_asSystem() throws Exception {
+    public void testEnqueuedRestrictedNotifications_hasPermission() throws Exception {
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
                 .thenReturn(true);
+        // SEND_CATEGORY_CAR_NOTIFICATIONS is a system-level permission that this test cannot
+        // obtain. Mocking out enforce permission call to ensure notifications can be created when
+        // permitted.
+        doNothing().when(mContext).enforceCallingPermission(
+                eq("android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"), anyString());
         List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
                 Notification.CATEGORY_CAR_WARNING,
                 Notification.CATEGORY_CAR_INFORMATION);
@@ -1114,7 +1120,6 @@
      */
     @Test
     public void testEnqueuedRestrictedNotifications_notAutomotive() throws Exception {
-        mService.isSystemUid = false;
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
                 .thenReturn(false);
         List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
@@ -1134,12 +1139,11 @@
     }
 
     /**
-     * Confirm if a non-system user tries to use the car categories on a automotive device that
-     * they will get a security exception
+     * Confirm if an application tries to use the car categories on a automotive device without the
+     * SEND_CATEGORY_CAR_NOTIFICATIONS permission that a security exception will be thrown.
      */
     @Test
-    public void testEnqueuedRestrictedNotifications_badUser() throws Exception {
-        mService.isSystemUid = false;
+    public void testEnqueuedRestrictedNotifications_noPermission() throws Exception {
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
                 .thenReturn(true);
         List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3c7206f..3430dbd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -44,6 +44,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.TestCase.assertTrue;
 import static junit.framework.TestCase.fail;
 
@@ -1556,6 +1557,49 @@
         assertEquals(zenRule.getName(), ruleInConfig.name);
     }
 
+    @Test
+    public void testRulesWithSameUri() {
+        Uri sharedUri = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        AutomaticZenRule zenRule = new AutomaticZenRule("name",
+                new ComponentName("android", "ScheduleConditionProvider"),
+                sharedUri,
+                NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+        String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+        AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
+                new ComponentName("android", "ScheduleConditionProvider"),
+                sharedUri,
+                NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+        String id2 = mZenModeHelperSpy.addAutomaticZenRule(zenRule2, "test");
+
+        Condition condition = new Condition(sharedUri, "", Condition.STATE_TRUE);
+        mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition);
+
+        for (ZenModeConfig.ZenRule rule : mZenModeHelperSpy.mConfig.automaticRules.values()) {
+            if (rule.id.equals(id)) {
+                assertNotNull(rule.condition);
+                assertTrue(rule.condition.state == Condition.STATE_TRUE);
+            }
+            if (rule.id.equals(id2)) {
+                assertNotNull(rule.condition);
+                assertTrue(rule.condition.state == Condition.STATE_TRUE);
+            }
+        }
+
+        condition = new Condition(sharedUri, "", Condition.STATE_FALSE);
+        mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition);
+
+        for (ZenModeConfig.ZenRule rule : mZenModeHelperSpy.mConfig.automaticRules.values()) {
+            if (rule.id.equals(id)) {
+                assertNotNull(rule.condition);
+                assertTrue(rule.condition.state == Condition.STATE_FALSE);
+            }
+            if (rule.id.equals(id2)) {
+                assertNotNull(rule.condition);
+                assertTrue(rule.condition.state == Condition.STATE_FALSE);
+            }
+        }
+    }
+
     private void setupZenConfig() {
         mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
new file mode 100644
index 0000000..f6c854e
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.slice.SliceManagerService.PackageMatchingCache;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class PackageMatchingCacheTest extends UiServiceTestCase {
+
+    private final Supplier<String> supplier = mock(Supplier.class);
+    private final PackageMatchingCache cache = new PackageMatchingCache(supplier);
+
+    @Test
+    public void testNulls() {
+        // Doesn't get for a null input
+        cache.matches(null);
+        verify(supplier, never()).get();
+
+        // Gets once valid input in sent.
+        cache.matches("");
+        verify(supplier).get();
+    }
+
+    @Test
+    public void testCaching() {
+        when(supplier.get()).thenReturn("ret.pkg");
+
+        assertTrue(cache.matches("ret.pkg"));
+        assertTrue(cache.matches("ret.pkg"));
+        assertTrue(cache.matches("ret.pkg"));
+
+        verify(supplier, times(1)).get();
+    }
+
+    @Test
+    public void testGetOnFailure() {
+        when(supplier.get()).thenReturn("ret.pkg");
+        assertTrue(cache.matches("ret.pkg"));
+
+        when(supplier.get()).thenReturn("other.pkg");
+        assertTrue(cache.matches("other.pkg"));
+        verify(supplier, times(2)).get();
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index cf1c36c..a443695 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -90,6 +90,8 @@
 
     @Test
     public void testAddPinCreatesPinned() throws RemoteException {
+        doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
@@ -97,6 +99,8 @@
 
     @Test
     public void testRemovePinDestroysPinned() throws RemoteException {
+        doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
 
         when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 09375db..89a0c7c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -73,7 +73,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 
@@ -1671,8 +1670,6 @@
     @Test
     public void testCanTurnScreenOn() {
         mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        doReturn(true).when(mStack).checkKeyguardVisibility(
-                same(mActivity), eq(true) /* shouldBeVisible */, anyBoolean());
         doReturn(true).when(mActivity).getTurnScreenOnFlag();
 
         assertTrue(mActivity.canTurnScreenOn());
@@ -1681,8 +1678,6 @@
     @Test
     public void testFreeformWindowCantTurnScreenOn() {
         mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        doReturn(true).when(mStack).checkKeyguardVisibility(
-                same(mActivity), eq(true) /* shouldBeVisible */, anyBoolean());
         doReturn(true).when(mActivity).getTurnScreenOnFlag();
 
         assertFalse(mActivity.canTurnScreenOn());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 4951658..a60f93a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1410,7 +1410,7 @@
                 new ActivityBuilder(mAtm).setTask(mTask).build();
         new ActivityBuilder(mAtm).setTask(mTask).build();
         doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
-        doReturn(true).when(nonTopVisibleActivity).shouldBeVisible(anyBoolean(), anyBoolean());
+        doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
         doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
                 anyBoolean());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7f90426..89a34cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -24,7 +24,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
 import static android.os.Build.VERSION_CODES.P;
 import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -762,13 +761,14 @@
                 window.mAttrs.screenOrientation, dc.getOrientation());
 
         // ----------------------------
-        // Test close-to-square display
+        // Test close-to-square display - should be handled in the same way
         // ----------------------------
         dc.mBaseDisplayHeight = dc.mBaseDisplayWidth;
         dc.configureDisplayPolicy();
 
-        assertEquals("Screen orientation must be SCREEN_ORIENTATION_USER.",
-                SCREEN_ORIENTATION_USER, dc.getOrientation());
+        assertEquals(
+                "Screen orientation must be defined by the window even on close-to-square display.",
+                window.mAttrs.screenOrientation, dc.getOrientation());
     }
 
     @Test
@@ -823,7 +823,14 @@
         final DisplayContent newDisplay = createNewDisplay();
 
         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
+        final Task stack = mDisplayContent.getTopStack();
+        final ActivityRecord activity = stack.topRunningActivity();
+        doReturn(true).when(activity).shouldBeVisibleUnchecked();
+
         final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
+        final Task stack1 = newDisplay.getTopStack();
+        final ActivityRecord activity1 = stack1.topRunningActivity();
+        doReturn(true).when(activity1).shouldBeVisibleUnchecked();
         appWin.setHasSurface(true);
         appWin1.setHasSurface(true);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 253fbae..58d994c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -44,6 +44,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -291,7 +292,8 @@
             mRecentTasks.add(mTasks.get(1));
             invocation.callRealMethod();
             return null;
-        }).when(mSupervisor).endActivityVisibilityUpdate();
+        }).when(mSupervisor).endActivityVisibilityUpdate(any(), anyInt(), anyBoolean(),
+                anyBoolean());
 
         mTaskContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                 false /* preserveWindows */, false /* notifyClients */);
@@ -1170,12 +1172,12 @@
                 () -> mAtm.setTaskWindowingModeSplitScreenPrimary(0, true));
         assertSecurityException(expectCallable,
                 () -> mAtm.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
-        assertSecurityException(expectCallable, () -> mAtm.getAllStackInfos());
+        assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos());
         assertSecurityException(expectCallable,
-                () -> mAtm.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+                () -> mAtm.getRootTaskInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
         assertSecurityException(expectCallable, () -> {
             try {
-                mAtm.getFocusedStackInfo();
+                mAtm.getFocusedRootTaskInfo();
             } catch (RemoteException e) {
                 // Ignore
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 29081d3..edcf0d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -138,8 +138,6 @@
     public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
         final int notchHeight = 100;
         setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
-        // Rotation is ignored so because the display size is close to square (700/600<1.333).
-        assertTrue(mActivity.mDisplayContent.ignoreRotationForApps());
 
         final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
         final float aspectRatio = 1.2f;
@@ -163,23 +161,14 @@
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertFitted();
 
-        // After the orientation of activity is changed, even display is not rotated, the aspect
-        // ratio should be the same (bounds=[0, 0 - 600, 600], appBounds=[0, 100 - 600, 600]).
+        // After the orientation of activity is changed, the display is rotated, the aspect
+        // ratio should be the same (bounds=[100, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
-        // The notch is still on top.
-        assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+        // The notch is no longer on top.
+        assertEquals(appBounds, mActivity.getBounds());
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
-
-        // Close-to-square display can rotate without being restricted by the requested orientation.
-        // The notch becomes on the left side. The activity is horizontal centered in 100 ~ 800.
-        // So the bounds and appBounds will be [200, 0 - 700, 600] (500x600) that is still fitted.
-        // Left = 100 + (800 - 100 - 500) / 2 = 200.
-        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
-        assertFitted();
-        assertEquals(appBounds.left,
-                notchHeight + (displayBounds.width() - notchHeight - appBounds.width()) / 2);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index eb7d9c2..6a29c5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -534,11 +534,14 @@
             doNothing().when(this).scheduleIdleTimeout(any());
             // unit test version does not handle launch wake lock
             doNothing().when(this).acquireLaunchWakelock();
-            doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
 
             mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
 
             initialize();
+
+            final KeyguardController controller = getKeyguardController();
+            spyOn(controller);
+            doReturn(true).when(controller).checkKeyguardVisibility(any());
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index e39b4bc..d37f3f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -17,17 +17,16 @@
 package com.android.server.wm;
 
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
-import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IScrollCaptureController;
 import android.view.IWindow;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.window.ClientWindowFrames;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -38,10 +37,9 @@
     }
 
     @Override
-    public void resized(Rect frame, Rect contentInsets, Rect visibleInsets,
-            Rect stableInsets, boolean reportDraw, MergedConfiguration mergedConfig,
-            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
-            DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {
+    public void resized(ClientWindowFrames frames, boolean reportDraw,
+            MergedConfiguration mergedConfig, boolean forceLayout, boolean alwaysConsumeSystemBars,
+            int displayId) throws RemoteException {
     }
 
     @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 38909f6..11eaf8c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -52,7 +52,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.PictureInPictureParams;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -229,58 +229,32 @@
         final Task task3 = createTask(stack3);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
-        // First organizer is registered, verify a task appears when changing windowing mode
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer, times(1))
+        // verify that tasks are appeared on registration
+        verify(organizer, times(3))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
         assertTrue(stack.isOrganized());
 
         // Now we replace the registration and1 verify the new organizer receives tasks
-        // newly entering the windowing mode.
         final ITaskOrganizer organizer2 = registerMockOrganizer();
-        stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        // One each for task and task2
-        verify(organizer2, times(2))
+        verify(organizer2, times(3))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
         verify(organizer2, times(0)).onTaskVanished(any());
         // One for task
-        verify(organizer).onTaskVanished(any());
+        verify(organizer, times(3)).onTaskVanished(any());
         assertTrue(stack2.isOrganized());
 
         // Now we unregister the second one, the first one should automatically be reregistered
         // so we verify that it's now seeing changes.
         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
-        verify(organizer, times(3))
+        verify(organizer, times(6))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        verify(organizer2, times(2)).onTaskVanished(any());
-
-        stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer, times(4))
-                .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        verify(organizer2, times(2)).onTaskVanished(any());
-        assertTrue(stack3.isOrganized());
-    }
-
-    @Test
-    public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
-        final ITaskOrganizer organizer = registerMockOrganizer();
-
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final Task task2 = createTask(stack);
-        stack.setWindowingMode(WINDOWING_MODE_PINNED);
-        verify(organizer, times(1))
-                .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-
-        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        verify(organizer, times(1)).onTaskVanished(any());
+        verify(organizer2, times(3)).onTaskVanished(any());
     }
 
     @Test
     public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
         final Task stack = createStack();
         final Task task = createTask(stack);
-        stack.setWindowingMode(WINDOWING_MODE_PINNED);
 
         final ITaskOrganizer organizer = registerMockOrganizer();
         verify(organizer, times(1))
@@ -301,9 +275,9 @@
         removeGlobalMinSizeRestriction();
         final Task stack = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        StackInfo info =
-                mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-        assertEquals(stack.mRemoteToken.toWindowContainerToken(), info.stackToken);
+        RootTaskInfo info =
+                mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+        assertEquals(stack.mRemoteToken.toWindowContainerToken(), info.token);
         testTransaction(stack);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 3106ca2..c18043f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -581,12 +581,10 @@
         mWm.mResizingWindows.remove(win);
         spyOn(win.mClient);
         try {
-            doThrow(new RemoteException("test")).when(win.mClient).resized(any() /* frame */,
-                    any() /* contentInsets */, any() /* visibleInsets */, any() /* stableInsets */,
+            doThrow(new RemoteException("test")).when(win.mClient).resized(any() /* frames */,
                     anyBoolean() /* reportDraw */, any() /* mergedConfig */,
-                    any() /* backDropFrame */, anyBoolean() /* forceLayout */,
-                    anyBoolean() /* alwaysConsumeSystemBars */, anyInt() /* displayId */,
-                    any() /* displayCutout */);
+                    anyBoolean() /* forceLayout */, anyBoolean() /* alwaysConsumeSystemBars */,
+                    anyInt() /* displayId */);
         } catch (RemoteException ignored) {
         }
         win.reportResized();
diff --git a/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl b/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl
new file mode 100644
index 0000000..50bbf4c
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/IDeviceIdleControllerAdapter.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.internal.telecom;
+
+/*
+ * Adapter interface for using DeviceIdleController, since the PowerWhitelistManager is not
+ * directly accessible in the SYSTEM process.
+ */
+interface IDeviceIdleControllerAdapter {
+    void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle,
+            String reason);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java b/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl
similarity index 60%
copy from packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
copy to telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl
index e65f19d..b560106 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
+++ b/telecomm/java/com/android/internal/telecom/IInternalServiceRetriever.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.keyguard.dagger;
+package com.android.internal.telecom;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.internal.telecom.IDeviceIdleControllerAdapter;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface ContainerView {
+/*
+ * Interface used to retrieve services that are only accessible via LocalService in the SYSTEM
+ * process.
+ */
+interface IInternalServiceRetriever {
+    IDeviceIdleControllerAdapter getDeviceIdleController();
 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
new file mode 100644
index 0000000..eda0f5b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ITelecomLoader.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.internal.telecom;
+
+import com.android.internal.telecom.ITelecomService;
+import com.android.internal.telecom.IInternalServiceRetriever;
+
+/*
+ * Internal interface for getting an instance of the ITelecomService for external publication.
+ * Allows the TelecomLoaderService to pass additional dependencies required for creation.
+ */
+interface ITelecomLoader {
+    ITelecomService createTelecomService(IInternalServiceRetriever retriever);
+}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 943d783..a53ea16 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -28,7 +28,6 @@
         "flickertestapplib",
         "flickerlib",
         "truth-prebuilt",
-        "app-helpers-core",
         "launcher-helper-lib",
         "launcher-aosp-tapl"
     ],
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
deleted file mode 100644
index 7147577..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.server.wm.flicker.helpers
-
-import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import com.android.server.wm.flicker.StandardAppHelper
-
-abstract class FlickerAppHelper(
-    instr: Instrumentation,
-    launcherName: String,
-    launcherStrategy: ILauncherStrategy
-) : StandardAppHelper(instr, sFlickerPackage, launcherName, launcherStrategy) {
-    companion object {
-        var sFlickerPackage = "com.android.server.wm.flicker.testapp"
-    }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index c1b7657..f4de36e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -30,7 +30,7 @@
     launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
             .getInstance(instr)
             .launcherStrategy
-) : FlickerAppHelper(instr, launcherName, launcherStrategy) {
+) : StandardAppHelper(instr, launcherName, launcherStrategy) {
     open fun openIME(device: UiDevice) {
         val editText = device.wait(
                 Until.findObject(By.res(getPackage(), "plain_text_input")),
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index d10bb1e..0572a78 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -28,7 +28,7 @@
     launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
             .getInstance(instr)
             .launcherStrategy
-) : FlickerAppHelper(instr, "PipApp", launcherStrategy) {
+) : StandardAppHelper(instr, "PipApp", launcherStrategy) {
     fun clickEnterPipButton(device: UiDevice) {
         val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"))
         Assert.assertNotNull("Pip button not found, this usually happens when the device " +
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
index 7d70812..98e05d5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.launch
 
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.dsl.LayersAssertion
 import com.android.server.wm.flicker.dsl.WmAssertion
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index afadb58..acd141a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -18,7 +18,7 @@
 
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 0ca1508..99218c2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -20,7 +20,7 @@
 import android.view.Surface
 import com.android.server.wm.flicker.NonRotationTestBase.Companion.SCREENSHOT_LAYER
 import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.WindowUtils
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index c5e48d9..3b5e669 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -19,7 +19,7 @@
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.exitSplitScreen
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index 91211ca..abf41a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -24,7 +24,7 @@
 import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
 import com.android.server.wm.flicker.FlickerTestBase
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.ImeAppHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index 5c7dcd9..87c8633 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -19,7 +19,7 @@
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.StandardAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.exitSplitScreen
diff --git a/tests/SilkFX/res/layout/activity_glass.xml b/tests/SilkFX/res/layout/activity_glass.xml
index a7b76bd..aa09f27 100644
--- a/tests/SilkFX/res/layout/activity_glass.xml
+++ b/tests/SilkFX/res/layout/activity_glass.xml
@@ -41,7 +41,16 @@
         app:layout_constraintBottom_toTopOf="@+id/bottomPanel"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintTop_toTopOf="parent">
+        <TextView
+            android:id="@+id/textOverlay"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18dp"
+            android:layout_gravity="center"
+            android:textColor="#ffffff"
+            android:text="Lorem Ipsum dolor sit amet." />
+    </com.android.test.silkfx.materials.GlassView>
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/bottomPanel"
@@ -90,7 +99,7 @@
         android:layout_marginEnd="12dp"
         android:layout_marginStart="12dp"
         android:max="150"
-        android:progress="20"
+        android:progress="40"
         app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="1.0"
@@ -118,7 +127,7 @@
         android:layout_marginEnd="12dp"
         android:layout_marginBottom="24dp"
         android:max="100"
-        android:progress="5"
+        android:progress="15"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0.0"
@@ -141,7 +150,7 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="24dp"
         android:layout_marginBottom="8dp"
-        android:text="Material Opacity"
+        android:text="Soft light Opacity"
         android:textColor="@android:color/white"
         app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
         app:layout_constraintStart_toStartOf="parent" />
@@ -219,6 +228,19 @@
         app:layout_constraintStart_toEndOf="@+id/background2"
         android:src="@drawable/background3" />
 
+    <Button
+        android:id="@+id/pickImage"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:scaleType="centerCrop"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onPickImageClick"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background3"
+        android:text="Pick file" />
+
     <Switch
         android:id="@+id/lightMaterialSwitch"
         android:layout_width="wrap_content"
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
index 72b342c..dde245f 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
@@ -16,6 +16,7 @@
 package com.android.test.silkfx.materials
 
 import android.app.Activity
+import android.content.Intent
 import android.graphics.Bitmap
 import android.graphics.BitmapFactory
 import android.graphics.Color
@@ -46,8 +47,14 @@
     lateinit var scrimOpacityValue: TextView
     lateinit var blurRadiusValue: TextView
     lateinit var zoomValue: TextView
+    lateinit var textOverlay: TextView
 
-    lateinit var background: Bitmap
+    var background: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+    set(value) {
+        field = value
+        backgroundView.setImageBitmap(background)
+        materialView.backgroundBitmap = background
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -68,10 +75,9 @@
         scrimOpacityValue = requireViewById(R.id.scrimOpacityValue)
         blurRadiusValue = requireViewById(R.id.blurRadiusValue)
         zoomValue = requireViewById(R.id.zoomValue)
+        textOverlay = requireViewById(R.id.textOverlay)
 
         background = BitmapFactory.decodeResource(resources, R.drawable.background1)
-        backgroundView.setImageBitmap(background)
-        materialView.backgroundBitmap = background
 
         blurRadiusSeekBar.setOnSeekBarChangeListener(this)
         materialOpacitySeekBar.setOnSeekBarChangeListener(this)
@@ -86,6 +92,7 @@
 
         lightMaterialSwitch.setOnCheckedChangeListener { _, isChecked ->
             materialView.color = if (isChecked) Color.WHITE else Color.BLACK
+            textOverlay.setTextColor(if (isChecked) Color.BLACK else Color.WHITE)
         }
     }
 
@@ -116,6 +123,11 @@
         }
     }
 
+    override fun onStop() {
+        super.onStop()
+        materialView.resetGyroOffsets()
+    }
+
     override fun onStartTrackingTouch(seekBar: SeekBar?) {}
     override fun onStopTrackingTouch(seekBar: SeekBar?) {}
 
@@ -128,7 +140,23 @@
         }
 
         background = BitmapFactory.decodeResource(resources, resource)
-        backgroundView.setImageBitmap(background)
-        materialView.backgroundBitmap = background
+    }
+
+    fun onPickImageClick(view: View) {
+        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+            addCategory(Intent.CATEGORY_OPENABLE)
+            type = "image/*"
+        }
+        startActivityForResult(intent, 0)
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        if (resultCode === RESULT_OK) {
+            data?.data?.also {
+                contentResolver.openFileDescriptor(it, "r").let {
+                    background = BitmapFactory.decodeFileDescriptor(it?.fileDescriptor)
+                }
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 6079768..71175847 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -25,23 +25,83 @@
 import android.graphics.Color
 import android.graphics.Outline
 import android.graphics.Paint
+import android.graphics.RadialGradient
 import android.graphics.Rect
 import android.graphics.Shader
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
 import android.util.AttributeSet
 import android.view.View
 import android.view.ViewOutlineProvider
+import android.widget.FrameLayout
+import com.android.internal.graphics.ColorUtils
 import com.android.test.silkfx.R
+import kotlin.math.sin
+import kotlin.math.sqrt
 
-class GlassView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
+class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(context, attributeSet) {
 
-    var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
-    var materialPaint = Paint()
-    var scrimPaint = Paint()
-    var noisePaint = Paint()
-    var blurPaint = Paint()
+    private val textureTranslationMultiplier = 200f
 
-    val src = Rect()
-    val dst = Rect()
+    private var gyroXRotation = 0f
+    private var gyroYRotation = 0f
+
+    private var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
+    private var materialPaint = Paint()
+    private var scrimPaint = Paint()
+    private var noisePaint = Paint()
+    private var blurPaint = Paint()
+
+    private val src = Rect()
+    private val dst = Rect()
+
+    private val sensorManager = context.getSystemService(SensorManager::class.java)
+    private val sensorListener = object : SensorEventListener {
+
+        // Constant to convert nanoseconds to seconds.
+        private val NS2S = 1.0f / 1000000000.0f
+        private val EPSILON = 0.000001f
+        private var timestamp: Float = 0f
+
+        override fun onSensorChanged(event: SensorEvent?) {
+            // This timestep's delta rotation to be multiplied by the current rotation
+            // after computing it from the gyro sample data.
+            if (timestamp != 0f && event != null) {
+                val dT = (event.timestamp - timestamp) * NS2S
+                // Axis of the rotation sample, not normalized yet.
+                var axisX: Float = event.values[0]
+                var axisY: Float = event.values[1]
+                var axisZ: Float = event.values[2]
+
+                // Calculate the angular speed of the sample
+                val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)
+
+                // Normalize the rotation vector if it's big enough to get the axis
+                // (that is, EPSILON should represent your maximum allowable margin of error)
+                if (omegaMagnitude > EPSILON) {
+                    axisX /= omegaMagnitude
+                    axisY /= omegaMagnitude
+                    axisZ /= omegaMagnitude
+                }
+
+                // Integrate around this axis with the angular speed by the timestep
+                // in order to get a delta rotation from this sample over the timestep
+                // We will convert this axis-angle representation of the delta rotation
+                // into a quaternion before turning it into the rotation matrix.
+                val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
+                val sinThetaOverTwo: Float = sin(thetaOverTwo)
+                gyroXRotation += sinThetaOverTwo * axisX
+                gyroYRotation += sinThetaOverTwo * axisY
+
+                invalidate()
+            }
+            timestamp = event?.timestamp?.toFloat() ?: 0f
+        }
+
+        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { }
+    }
 
     var backgroundBitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
     set(value) {
@@ -97,6 +157,7 @@
     }
 
     init {
+        setWillNotDraw(false)
         materialPaint.blendMode = BlendMode.SOFT_LIGHT
         noisePaint.blendMode = BlendMode.SOFT_LIGHT
         noisePaint.shader = BitmapShader(noise, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
@@ -112,12 +173,25 @@
         clipToOutline = true
     }
 
+    override fun onAttachedToWindow() {
+        sensorManager?.getSensorList(Sensor.TYPE_GYROSCOPE)?.firstOrNull().let {
+            sensorManager?.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_GAME)
+        }
+    }
+
+    override fun onDetachedFromWindow() {
+        sensorManager?.unregisterListener(sensorListener)
+    }
+
     override fun onDraw(canvas: Canvas?) {
-        src.set(-width/2, -height/2, width/2, height/2)
+        src.set(-width / 2, -height / 2, width / 2, height / 2)
         src.scale(1.0f + zoom)
         val centerX = left + width / 2
         val centerY = top + height / 2
-        src.set(src.left + centerX, src.top + centerY, src.right + centerX, src.bottom + centerY)
+        val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
+        val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
+        src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
+                src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
 
         dst.set(0, 0, width, height)
         canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
@@ -125,4 +199,10 @@
         canvas?.drawRect(dst, noisePaint)
         canvas?.drawRect(dst, scrimPaint)
     }
+
+    fun resetGyroOffsets() {
+        gyroXRotation = 0f
+        gyroYRotation = 0f
+        invalidate()
+    }
 }
\ No newline at end of file
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index d0742c7..3f5c673 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -144,6 +144,7 @@
 
   @Deprecated public static class WifiConfiguration.GroupCipher {
     field @Deprecated public static final int CCMP = 3; // 0x3
+    field @Deprecated public static final int GCMP_128 = 7; // 0x7
     field @Deprecated public static final int GCMP_256 = 5; // 0x5
     field @Deprecated public static final int SMS4 = 6; // 0x6
     field @Deprecated public static final int TKIP = 2; // 0x2
@@ -173,6 +174,7 @@
 
   @Deprecated public static class WifiConfiguration.PairwiseCipher {
     field @Deprecated public static final int CCMP = 2; // 0x2
+    field @Deprecated public static final int GCMP_128 = 5; // 0x5
     field @Deprecated public static final int GCMP_256 = 3; // 0x3
     field @Deprecated public static final int NONE = 0; // 0x0
     field @Deprecated public static final int SMS4 = 4; // 0x4
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index eff64a3..c3e573c 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -449,6 +449,7 @@
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>);
+    method public boolean is60GHzBandSupported();
     method public boolean isApMacRandomizationSupported();
     method public boolean isConnectedMacRandomizationSupported();
     method @Deprecated public boolean isDeviceToDeviceRttSupported();
@@ -651,6 +652,7 @@
     field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
     field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
     field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
+    field public static final int WIFI_BAND_60_GHZ = 16; // 0x10
     field public static final int WIFI_BAND_6_GHZ = 8; // 0x8
     field public static final int WIFI_BAND_BOTH = 3; // 0x3
     field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index e493789..b3ed8ac 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -118,6 +118,8 @@
 
     boolean is6GHzBandSupported();
 
+    boolean is60GHzBandSupported();
+
     boolean isWifiStandardSupported(int standard);
 
     DhcpInfo getDhcpInfo();
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 656444e..02b7a42 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -301,9 +301,16 @@
          */
         public static final int SMS4 = 4;
 
+        /**
+         * AES in Galois/Counter Mode with a 128-bit integrity key
+         */
+        public static final int GCMP_128 = 5;
+
+
         public static final String varName = "pairwise";
 
-        public static final String[] strings = { "NONE", "TKIP", "CCMP", "GCMP_256", "SMS4" };
+        public static final String[] strings = { "NONE", "TKIP", "CCMP", "GCMP_256", "SMS4",
+                "GCMP_128" };
     }
 
     /**
@@ -345,13 +352,17 @@
          * SMS4 cipher for WAPI
          */
         public static final int SMS4 = 6;
+        /**
+         * AES in Galois/Counter Mode with a 128-bit integrity key
+         */
+        public static final int GCMP_128 = 7;
 
         public static final String varName = "group";
 
         public static final String[] strings =
                 { /* deprecated */ "WEP40", /* deprecated */ "WEP104",
                         "TKIP", "CCMP", "GTK_NOT_USED", "GCMP_256",
-                        "SMS4" };
+                        "SMS4", "GCMP_128" };
     }
 
     /**
@@ -498,8 +509,10 @@
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_128);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_128);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                 requirePmf = true;
                 break;
@@ -508,7 +521,9 @@
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_128);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_128);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                 allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
                 // Note: allowedSuiteBCiphers bitset will be set by the service once the
@@ -519,8 +534,10 @@
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_128);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+                allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_128);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
                 requirePmf = true;
                 break;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b28b902..c76f4a6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2407,6 +2407,8 @@
     public static final long WIFI_FEATURE_OCE              = 0x1000000000L; // OCE Support
     /** @hide */
     public static final long WIFI_FEATURE_WAPI             = 0x2000000000L; // WAPI
+    /** @hide */
+    public static final long WIFI_FEATURE_INFRA_60G        = 0x4000000000L; // 60 GHz Band Support
 
     /** @hide */
     public static final long WIFI_FEATURE_FILS_SHA256     = 0x4000000000L; // FILS-SHA256
@@ -2569,6 +2571,21 @@
     }
 
     /**
+     * Check if the chipset supports the 60GHz frequency band.
+     *
+     * @return {@code true} if supported, {@code false} otherwise.
+     * @hide
+     */
+    @SystemApi
+    public boolean is60GHzBandSupported() {
+        try {
+            return mService.is60GHzBandSupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check if the chipset supports 6GHz band.
      * @return {@code true} if supported, {@code false} otherwise.
      */
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index aa69963..e992c83 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -176,7 +176,7 @@
             mWapiEnterpriseConfig = null;
             mIsNetworkUntrusted = false;
             mPriorityGroup = 0;
-            mIsEnhancedMacRandomizationEnabled = true;
+            mIsEnhancedMacRandomizationEnabled = false;
         }
 
         /**
@@ -409,11 +409,12 @@
          * Suggested networks will never use the device (factory) MAC address to associate to the
          * network - instead they use a locally generated random MAC address. This method controls
          * the strategy for generating the random MAC address:
-         * <li> Persisted MAC randomization (false): generates the MAC address from a secret seed
-         * and information from the Wi-Fi configuration (SSID or Passpoint profile). That means that
-         * the same generated MAC address will be used for each subsequent association. </li>
-         * <li> Enhanced MAC randomization (true - the default): periodically generates a new MAC
-         * address new connections. Under this option, the randomized MAC address should change
+         * <li> Persisted MAC randomization (false - the default): generates the MAC address from a
+         * secret seed and information from the Wi-Fi configuration (SSID or Passpoint profile).
+         * This means that the same generated MAC address will be used for each subsequent
+         * association. </li>
+         * <li> Enhanced MAC randomization (true): periodically generates a new MAC
+         * address for new connections. Under this option, the randomized MAC address should change
          * if the suggestion is removed and then added back. </li>
          *
          * @param enabled {@code true} to periodically change the randomized MAC address.
@@ -731,6 +732,8 @@
                             + "suggestion with Passpoint configuration");
                 }
                 wifiConfiguration = buildWifiConfigurationForPasspoint();
+                mPasspointConfiguration.setEnhancedMacRandomizationEnabled(
+                        mIsEnhancedMacRandomizationEnabled);
             } else {
                 if (mSsid == null) {
                     throw new IllegalStateException("setSsid should be invoked for suggestion");
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 94771ac..a68d7e2 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -68,7 +68,9 @@
     /** @hide */
     public static final int WIFI_BAND_INDEX_6_GHZ = 3;
     /** @hide */
-    public static final int WIFI_BAND_COUNT = 4;
+    public static final int WIFI_BAND_INDEX_60_GHZ = 4;
+    /** @hide */
+    public static final int WIFI_BAND_COUNT = 5;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -76,7 +78,8 @@
             WIFI_BAND_INDEX_24_GHZ,
             WIFI_BAND_INDEX_5_GHZ,
             WIFI_BAND_INDEX_5_GHZ_DFS_ONLY,
-            WIFI_BAND_INDEX_6_GHZ})
+            WIFI_BAND_INDEX_6_GHZ,
+            WIFI_BAND_INDEX_60_GHZ})
     public @interface WifiBandIndex {}
 
     /** no band specified; use channel list instead */
@@ -89,6 +92,8 @@
     public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
     /** 6 GHz band */
     public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ;
+    /** 60 GHz band */
+    public static final int WIFI_BAND_60_GHZ = 1 << WIFI_BAND_INDEX_60_GHZ;
 
     /**
      * Combination of bands
@@ -113,6 +118,12 @@
     /** 2.4 GHz band and 5 GHz band; with DFS channels and 6 GHz */
     public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ =
             WIFI_BAND_BOTH_WITH_DFS | WIFI_BAND_6_GHZ;
+    /** @hide */
+    public static final int WIFI_BAND_24_5_6_60_GHZ =
+            WIFI_BAND_24_5_6_GHZ | WIFI_BAND_60_GHZ;
+    /** @hide */
+    public static final int WIFI_BAND_24_5_WITH_DFS_6_60_GHZ =
+            WIFI_BAND_24_5_6_60_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -127,7 +138,10 @@
             WIFI_BAND_BOTH_WITH_DFS,
             WIFI_BAND_6_GHZ,
             WIFI_BAND_24_5_6_GHZ,
-            WIFI_BAND_24_5_WITH_DFS_6_GHZ})
+            WIFI_BAND_24_5_WITH_DFS_6_GHZ,
+            WIFI_BAND_60_GHZ,
+            WIFI_BAND_24_5_6_60_GHZ,
+            WIFI_BAND_24_5_WITH_DFS_6_60_GHZ})
     public @interface WifiBand {}
 
     /**
@@ -179,7 +193,10 @@
      * @hide
      */
     public static boolean isFullBandScan(@WifiBand int bandScanned, boolean excludeDfs) {
-        return (bandScanned | WIFI_BAND_6_GHZ | (excludeDfs ? WIFI_BAND_5_GHZ_DFS_ONLY : 0))
+        // 5GHz DFS channel is part of 5GHz, mark 5GHz scanned as well.
+        if ((bandScanned & WIFI_BAND_5_GHZ_DFS_ONLY) != 0) bandScanned |= WIFI_BAND_5_GHZ;
+        return (bandScanned | WIFI_BAND_6_GHZ | WIFI_BAND_60_GHZ
+                | (excludeDfs ? WIFI_BAND_5_GHZ_DFS_ONLY : 0))
                 == WIFI_BAND_ALL;
     }
 
@@ -571,6 +588,19 @@
             }
         }
 
+        /** {@hide} */
+        public void addResults(@NonNull ScanData s) {
+            mBandScanned |= s.mBandScanned;
+            mFlags |= s.mFlags;
+            addResults(s.getResults());
+        }
+
+        /** {@hide} */
+        public boolean isFullBandScanResults() {
+            return (mBandScanned & WifiScanner.WIFI_BAND_24_GHZ) != 0
+                && (mBandScanned & WifiScanner.WIFI_BAND_5_GHZ) != 0;
+        }
+
         /** Implement the Parcelable interface {@hide} */
         public int describeContents() {
             return 0;
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index d1d1780..61a6e16 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -444,6 +444,11 @@
     private boolean mIsMacRandomizationEnabled = true;
 
     /**
+     * Whether this passpoint configuration should use enhanced MAC randomization.
+     */
+    private boolean mIsEnhancedMacRandomizationEnabled = false;
+
+    /**
      * Indicates if the end user has expressed an explicit opinion about the
      * meteredness of this network, such as through the Settings app.
      * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED},
@@ -481,6 +486,20 @@
     }
 
     /**
+     * This setting is only applicable if MAC randomization is enabled.
+     * If set to true, the framework will periodically generate new MAC addresses for new
+     * connections.
+     * If set to false (the default), the framework will use the same locally generated MAC address
+     * for connections to this passpoint configuration.
+     * @param enabled true to use enhanced MAC randomization, false to use persistent MAC
+     *                randomization.
+     * @hide
+     */
+    public void setEnhancedMacRandomizationEnabled(boolean enabled) {
+        mIsEnhancedMacRandomizationEnabled = enabled;
+    }
+
+    /**
      * Sets the metered override setting for this Passpoint configuration.
      *
      * @param meteredOverride One of the values in {@link MeteredOverride}
@@ -531,6 +550,19 @@
     }
 
     /**
+     * When MAC randomization is enabled, this indicates whether enhanced MAC randomization or
+     * persistent MAC randomization will be used for connections to this Passpoint network.
+     * If true, the MAC address used for connections will periodically change. Otherwise, the same
+     * locally generated MAC will be used for all connections to this passpoint configuration.
+     *
+     * @return true for enhanced MAC randomization enabled. False for disabled.
+     * @hide
+     */
+    public boolean isEnhancedMacRandomizationEnabled() {
+        return mIsEnhancedMacRandomizationEnabled;
+    }
+
+    /**
      * Constructor for creating PasspointConfiguration with default values.
      */
     public PasspointConfiguration() {}
@@ -574,6 +606,7 @@
         mCarrierId = source.mCarrierId;
         mIsAutojoinEnabled = source.mIsAutojoinEnabled;
         mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
+        mIsEnhancedMacRandomizationEnabled = source.mIsEnhancedMacRandomizationEnabled;
         mMeteredOverride = source.mMeteredOverride;
     }
 
@@ -606,6 +639,7 @@
         dest.writeInt(mCarrierId);
         dest.writeBoolean(mIsAutojoinEnabled);
         dest.writeBoolean(mIsMacRandomizationEnabled);
+        dest.writeBoolean(mIsEnhancedMacRandomizationEnabled);
         dest.writeInt(mMeteredOverride);
     }
 
@@ -639,6 +673,7 @@
                 && mCarrierId == that.mCarrierId
                 && mIsAutojoinEnabled == that.mIsAutojoinEnabled
                 && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
+                && mIsEnhancedMacRandomizationEnabled == that.mIsEnhancedMacRandomizationEnabled
                 && mMeteredOverride == that.mMeteredOverride
                 && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
                 : mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
@@ -651,7 +686,7 @@
                 mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes,
                 mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
                 mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled,
-                mMeteredOverride);
+                mIsEnhancedMacRandomizationEnabled, mMeteredOverride);
     }
 
     @Override
@@ -707,6 +742,7 @@
         builder.append("CarrierId:" + mCarrierId);
         builder.append("IsAutojoinEnabled:" + mIsAutojoinEnabled);
         builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
+        builder.append("mIsEnhancedMacRandomizationEnabled:" + mIsEnhancedMacRandomizationEnabled);
         builder.append("mMeteredOverride:" + mMeteredOverride);
         return builder.toString();
     }
@@ -815,6 +851,7 @@
                 config.mCarrierId = in.readInt();
                 config.mIsAutojoinEnabled = in.readBoolean();
                 config.mIsMacRandomizationEnabled = in.readBoolean();
+                config.mIsEnhancedMacRandomizationEnabled = in.readBoolean();
                 config.mMeteredOverride = in.readInt();
                 return config;
             }
diff --git a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java b/wifi/java/android/net/wifi/nl80211/NativeScanResult.java
index a8e9999..35cf45f 100644
--- a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java
+++ b/wifi/java/android/net/wifi/nl80211/NativeScanResult.java
@@ -225,6 +225,15 @@
      * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Immediate Block Ack.
      */
     public static final int BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK = 0x1 << 15;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): DMG ESS.
+     * In DMG bits 0 and 1 are parsed together, where ESS=0x3 and IBSS=0x1
+     */
+    public static final int BSS_CAPABILITY_DMG_ESS = 0x3;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): DMG IBSS.
+     */
+    public static final int BSS_CAPABILITY_DMG_IBSS = 0x1;
 
     /**
      *  Returns the capabilities of the AP repseresented by this scan result as advertised in the
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
index 3175e45..fb58d3f 100644
--- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1048,6 +1048,7 @@
      * {@link WifiScanner#WIFI_BAND_5_GHZ},
      * {@link WifiScanner#WIFI_BAND_5_GHZ_DFS_ONLY},
      * {@link WifiScanner#WIFI_BAND_6_GHZ}
+     * {@link WifiScanner.WIFI_BAND_60_GHZ}
      * @return frequencies vector of valid frequencies (MHz), or an empty array for error.
      * @throws IllegalArgumentException if band is not recognized.
      */
@@ -1071,6 +1072,9 @@
                 case WifiScanner.WIFI_BAND_6_GHZ:
                     result = mWificond.getAvailable6gChannels();
                     break;
+                case WifiScanner.WIFI_BAND_60_GHZ:
+                    result = mWificond.getAvailable60gChannels();
+                    break;
                 default:
                     throw new IllegalArgumentException("unsupported band " + band);
             }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index abb9ce6..668d238 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -682,15 +682,15 @@
     }
 
     /**
-     * Verify that the macRandomizationSetting defaults to RANDOMIZATION_ENHANCED and could be set
-     * to RANDOMIZATION_PERSISTENT.
+     * Verify that the macRandomizationSetting defaults to RANDOMIZATION_PERSISTENT and could be set
+     * to RANDOMIZATION_ENHANCED.
      */
     @Test
     public void testWifiNetworkSuggestionBuilderSetMacRandomization() {
         WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
                 .setSsid(TEST_SSID)
                 .build();
-        assertEquals(WifiConfiguration.RANDOMIZATION_ENHANCED,
+        assertEquals(WifiConfiguration.RANDOMIZATION_PERSISTENT,
                 suggestion.wifiConfiguration.macRandomizationSetting);
 
         suggestion = new WifiNetworkSuggestion.Builder()
@@ -709,6 +709,31 @@
     }
 
     /**
+     * Verify that the builder creates the appropriate PasspointConfiguration according to the
+     * enhanced MAC randomization setting.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderSetMacRandomizationPasspoint() {
+        PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+                .setPasspointConfig(passpointConfiguration)
+                .build();
+        assertEquals(false, suggestion.passpointConfiguration.isEnhancedMacRandomizationEnabled());
+
+        suggestion = new WifiNetworkSuggestion.Builder()
+                .setPasspointConfig(passpointConfiguration)
+                .setIsEnhancedMacRandomizationEnabled(false)
+                .build();
+        assertEquals(false, suggestion.passpointConfiguration.isEnhancedMacRandomizationEnabled());
+
+        suggestion = new WifiNetworkSuggestion.Builder()
+                .setPasspointConfig(passpointConfiguration)
+                .setIsEnhancedMacRandomizationEnabled(true)
+                .build();
+        assertEquals(true, suggestion.passpointConfiguration.isEnhancedMacRandomizationEnabled());
+    }
+
+    /**
      * Check that parcel marshalling/unmarshalling works
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 8270d64..badcf52 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -186,6 +186,7 @@
         assertFalse(config.validateForR2());
         assertTrue(config.isAutojoinEnabled());
         assertTrue(config.isMacRandomizationEnabled());
+        assertFalse(config.isEnhancedMacRandomizationEnabled());
         assertTrue(config.getMeteredOverride() == METERED_OVERRIDE_NONE);
     }