Merge "Merge ab/AP4A.241205.013 into aosp-main-future" into aosp-main-future
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index ecd2da5..d38dac2 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -93,6 +93,7 @@
         "com.android.media.flags.editing-aconfig-java",
         "com.android.media.flags.performance-aconfig-java",
         "com.android.media.flags.projection-aconfig-java",
+        "com.android.net.http.flags-aconfig-exported-java",
         "com.android.net.thread.platform.flags-aconfig-java",
         "com.android.ranging.flags.ranging-aconfig-java-export",
         "com.android.server.contextualsearch.flags-java",
@@ -398,6 +399,8 @@
     min_sdk_version: "30",
     apex_available: [
         "//apex_available:platform",
+        "com.android.art",
+        "com.android.art.debug",
         "com.android.btservices",
         "com.android.mediaprovider",
         "com.android.permission",
diff --git a/core/api/current.txt b/core/api/current.txt
index 8ed47a2..e0c409a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16803,7 +16803,7 @@
     method public void arcTo(@NonNull android.graphics.RectF, float, float);
     method public void arcTo(float, float, float, float, float, float, boolean);
     method public void close();
-    method @Deprecated public void computeBounds(@NonNull android.graphics.RectF, boolean);
+    method public void computeBounds(@NonNull android.graphics.RectF, boolean);
     method @FlaggedApi("com.android.graphics.flags.exact_compute_bounds") public void computeBounds(@NonNull android.graphics.RectF);
     method public void conicTo(float, float, float, float, float);
     method public void cubicTo(float, float, float, float, float, float);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9393eb5..d4b1af0 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -506,6 +506,10 @@
     field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L
   }
 
+  public class UpdateEngine {
+    method @FlaggedApi("android.os.update_engine_api") public void triggerPostinstall(@NonNull String);
+  }
+
 }
 
 package android.os.storage {
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index f64a8117..11c54ef 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -193,4 +193,31 @@
             return false;
         }
     }
+
+    /**
+     * Gets the genfs labels version of the vendor. The genfs labels version is
+     * specified in {@code /vendor/etc/selinux/genfs_labels_version.txt}. The
+     * version follows the VINTF version format "YYYYMM" and affects how {@code
+     * genfs_contexts} entries are applied.
+     *
+     * <p>The genfs labels version indicates changes in the SELinux labeling
+     * scheme over time. For example:
+     * <ul>
+     *   <li>For version 202504 and later, {@code /sys/class/udc} is labeled as
+     *   {@code sysfs_udc}.
+     *   <li>For version 202404 and earlier, {@code /sys/class/udc} is labeled
+     *   as {@code sysfs}.
+     * </ul>
+     * Check {@code /system/etc/selinux/plat_sepolicy_genfs_{version}.cil} to
+     * see which labels are new in {version}.
+     *
+     * <p>Older vendors may override {@code genfs_contexts} with vendor-specific
+     * extensions. The framework must not break such labellings to maintain
+     * compatibility with such vendors, by checking the genfs labels version and
+     * implementing a fallback mechanism.
+     *
+     * @return an integer representing the genfs labels version of /vendor, in
+     *         the format YYYYMM.
+     */
+    public static final native int getGenfsLabelsVersion();
 }
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 0a8f62f..81e4549 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -667,4 +668,23 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Run postinstall script for specified partition |partition|
+     *
+     * @param partition The partition to trigger postinstall runs
+     *
+     * @throws ServiceSpecificException error code of this exception would be one of
+     * https://cs.android.com/android/platform/superproject/main/+/main:system/update_engine/common/error_code.h
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_UPDATE_ENGINE_API)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void triggerPostinstall(@NonNull String partition) {
+        try {
+            mUpdateEngine.triggerPostinstall(partition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index bd9ab86..6473cd8 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -2310,7 +2310,6 @@
         // -- parcelable interface --
 
         private RankingMap(Parcel in) {
-            final ClassLoader cl = getClass().getClassLoader();
             final int count = in.readInt();
             mOrderedKeys.ensureCapacity(count);
             mRankings.ensureCapacity(count);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index e402ddf..e60879e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -19,6 +19,8 @@
 import static android.system.OsConstants.S_IRWXG;
 import static android.system.OsConstants.S_IRWXO;
 
+import static android.net.http.Flags.preloadHttpengineInZygote;
+
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START;
 
@@ -27,6 +29,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.res.Resources;
 import android.os.Build;
+import android.net.http.HttpEngine;
 import android.os.Environment;
 import android.os.IInstalld;
 import android.os.Process;
@@ -144,6 +147,23 @@
         Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
         preloadSharedLibraries();
         preloadTextResources();
+
+        // TODO: remove the try/catch and the flag read as soon as the flag is ramped and 25Q2
+        // starts building from source.
+        if (preloadHttpengineInZygote()) {
+            try {
+                HttpEngine.preload();
+            } catch (NoSuchMethodError e){
+                // The flag protecting this API is not an exported
+                // flag because ZygoteInit happens before the
+                // system service has initialized the flag which means
+                // that we can't query the real value of the flag
+                // from the tethering module. In order to avoid crashing
+                // in the case where we have (new zygote, old tethering).
+                // we catch the NoSuchMethodError and just log.
+                Log.d(TAG, "HttpEngine.preload() threw " + e);
+            }
+        }
         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
         // for memory sharing purposes.
         WebViewFactory.prepareWebViewInZygote();
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9a4ff8f..37c84ce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -290,6 +290,7 @@
                 "libasync_safe",
                 "libbinderthreadstateutils",
                 "libdmabufinfo",
+                "libgenfslabelsversion.ffi",
                 "libgui_window_info_static",
                 "libkernelconfigs",
                 "libnativehelper_lazy",
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 7a4670f4..805d5ad 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -18,18 +18,19 @@
 
 #include <errno.h>
 #include <fcntl.h>
-
-#include <utils/Log.h>
-
+#include <genfslabelsversion.h>
 #include <nativehelper/JNIPlatformHelp.h>
-#include "jni.h"
-#include "core_jni_helpers.h"
-#include "selinux/selinux.h"
-#include "selinux/android.h"
-#include <memory>
-#include <atomic>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+
+#include <atomic>
+#include <memory>
+
+#include "core_jni_helpers.h"
+#include "jni.h"
+#include "selinux/android.h"
+#include "selinux/selinux.h"
 
 namespace android {
 namespace {
@@ -404,8 +405,19 @@
 }
 
 /*
+ * Function: getGenfsLabelsVersion
+ * Purpose: get which genfs labels version /vendor uses
+ * Returns: int: genfs labels version of /vendor
+ * Exceptions: none
+ */
+static jint getGenfsLabelsVersion(JNIEnv *, jclass) {
+    return get_genfs_labels_version();
+}
+
+/*
  * JNI registration.
  */
+// clang-format off
 static const JNINativeMethod method_table[] = {
     /* name,                     signature,                    funcPtr */
     { "checkSELinuxAccess"       , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
@@ -420,7 +432,9 @@
     { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
     { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
     { "fileSelabelLookup"        , "(Ljava/lang/String;)Ljava/lang/String;"       , (void*)fileSelabelLookup},
+    { "getGenfsLabelsVersion"    , "()I"                                          , (void *)getGenfsLabelsVersion},
 };
+// clang-format on
 
 static int log_callback(int type, const char *fmt, ...) {
     va_list ap;
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index a903ed9..335791c 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.fail;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -42,7 +43,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PowerManager.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
 public class BinderProxyTest {
     private static class CountingListener implements Binder.ProxyTransactListener {
         int mStartedCount;
@@ -62,7 +63,7 @@
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
     private Context mContext;
-    private PowerManager mPowerManager;
+    private ActivityManager mActivityManager;
 
     /**
      * Setup any common data for the upcoming tests.
@@ -70,7 +71,7 @@
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     @Test
@@ -80,7 +81,7 @@
         Binder.setProxyTransactListener(listener);
         Binder.setProxyTransactListener(null);
 
-        mPowerManager.isInteractive();
+        mActivityManager.isUserRunning(7); // something which does a binder call
 
         assertEquals(0, listener.mStartedCount);
         assertEquals(0, listener.mEndedCount);
@@ -92,7 +93,7 @@
         CountingListener listener = new CountingListener();
         Binder.setProxyTransactListener(listener);
 
-        mPowerManager.isInteractive();
+        mActivityManager.isUserRunning(27); // something which does a binder call
 
         assertEquals(1, listener.mStartedCount);
         assertEquals(1, listener.mEndedCount);
@@ -112,7 +113,7 @@
         });
 
         // Check it does not throw..
-        mPowerManager.isInteractive();
+        mActivityManager.isUserRunning(47); // something which does a binder call
     }
 
     private IBinder mRemoteBinder = null;
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 880f30c..7e8fab4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -505,7 +505,6 @@
         <permission name="android.permission.RENOUNCE_PERMISSIONS" />
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
         <permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
-        <permission name="android.permission.READ_DROPBOX_DATA" />
         <permission name="android.permission.READ_LOGS" />
         <permission name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
         <permission name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 073307c..d010c52 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -301,10 +301,7 @@
      *
      * @param bounds Returns the computed bounds of the path's control points.
      * @param exact This parameter is no longer used.
-     *
-     * @deprecated use computeBounds(RectF) instead
      */
-    @Deprecated
     @SuppressWarnings({"UnusedDeclaration"})
     public void computeBounds(@NonNull RectF bounds, boolean exact) {
         computeBounds(bounds);
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index 7a1cf92..aeba4df 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -17,13 +17,14 @@
 package android.media.projection;
 
 import android.media.projection.IMediaProjectionCallback;
+import android.media.projection.StopReason;
 import android.os.IBinder;
 import android.app.ActivityOptions.LaunchCookie;
 
 /** {@hide} */
 interface IMediaProjection {
     void start(IMediaProjectionCallback callback);
-    void stop();
+    void stop(StopReason stopReason);
 
     boolean canProjectAudio();
     boolean canProjectVideo();
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 3d927d3..1a7e734 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -16,11 +16,13 @@
 
 package android.media.projection;
 
+import android.graphics.Rect;
 import android.media.projection.IMediaProjection;
 import android.media.projection.IMediaProjectionCallback;
 import android.media.projection.IMediaProjectionWatcherCallback;
 import android.media.projection.MediaProjectionInfo;
 import android.media.projection.ReviewGrantedConsentResult;
+import android.media.projection.StopReason;
 import android.os.IBinder;
 import android.view.ContentRecordingSession;
 
@@ -106,12 +108,7 @@
     @EnforcePermission("MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    void stopActiveProjection();
-
-    @EnforcePermission("MANAGE_MEDIA_PROJECTION")
-    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
-            + ".permission.MANAGE_MEDIA_PROJECTION)")
-    void notifyActiveProjectionCapturedContentResized(int width, int height);
+    void stopActiveProjection(in StopReason stopReason);
 
     @EnforcePermission("MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
@@ -226,5 +223,11 @@
     @EnforcePermission("MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
-    void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode);
+    oneway void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode);
+
+    @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+            + ".permission.MANAGE_MEDIA_PROJECTION)")
+    oneway void notifyCaptureBoundsChanged(int contentToRecord, int targetProcessUid,
+            in Rect captureBounds);
 }
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index ef4c3ef..4f0e3a3 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -305,7 +305,7 @@
     public void stop() {
         try {
             Log.d(TAG, "Content Recording: stopping projection");
-            mImpl.stop();
+            mImpl.stop(StopReason.STOP_HOST_APP);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to stop projection", e);
         }
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index dc55e41..9cc2cca 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -297,10 +297,10 @@
      * Stop the current projection if there is one.
      * @hide
      */
-    public void stopActiveProjection() {
+    public void stopActiveProjection(@StopReason int stopReason) {
         try {
             Log.d(TAG, "Content Recording: stopping active projection");
-            mService.stopActiveProjection();
+            mService.stopActiveProjection(stopReason);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to stop the currently active media projection", e);
         }
diff --git a/media/java/android/media/projection/StopReason.aidl b/media/java/android/media/projection/StopReason.aidl
new file mode 100644
index 0000000..8611def
--- /dev/null
+++ b/media/java/android/media/projection/StopReason.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.projection;
+
+/**
+ * Identifies the reason for a MediaProjection being stopped (for metric logging purposes)
+ * @hide
+ */
+@Backing(type="int")
+enum StopReason {
+    STOP_UNKNOWN = 0,
+    STOP_HOST_APP = 1,
+    STOP_TARGET_REMOVED = 2,
+    STOP_DEVICE_LOCKED = 3,
+    STOP_PRIVACY_CHIP = 4,
+    STOP_QS_TILE = 5,
+    STOP_USER_SWITCH = 6,
+    STOP_FOREGROUND_SERVICE_CHANGE = 7,
+    STOP_NEW_PROJECTION = 8,
+    STOP_NEW_MEDIA_ROUTE = 9,
+    STOP_ERROR = 10,
+}
diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
index 6860c0b..e6f8aad 100644
--- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
+++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
@@ -44,7 +44,7 @@
     }
 
     @Override
-    public void stop() throws RemoteException {
+    public void stop(@StopReason int stopReason) throws RemoteException {
         // Pass along to the client's callback wrapper.
         mIMediaProjectionCallback.onStop();
     }
diff --git a/native/android/OWNERS b/native/android/OWNERS
index f0db2ea..1fde7d2 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -2,7 +2,7 @@
 
 # General NDK API reviewers
 per-file libandroid.map.txt = [email protected], [email protected], [email protected]
-per-file libandroid.map.txt = [email protected], [email protected]
+per-file libandroid.map.txt = [email protected], [email protected], [email protected]
 
 # Networking
 per-file libandroid_net.map.txt, net.c = set noparent
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 3ed9b76..e97b15d 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -124,6 +124,7 @@
 
   @FlaggedApi("android.nfc.nfc_oem_extension") public abstract class NfcRoutingTableEntry {
     method public int getNfceeId();
+    method public int getRouteType();
     method public int getType();
     field public static final int TYPE_AID = 0; // 0x0
     field public static final int TYPE_PROTOCOL = 1; // 0x1
diff --git a/nfc/java/android/nfc/Entry.java b/nfc/java/android/nfc/Entry.java
index 49d0f10..aa5ba58 100644
--- a/nfc/java/android/nfc/Entry.java
+++ b/nfc/java/android/nfc/Entry.java
@@ -25,11 +25,13 @@
     private final byte mType;
     private final byte mNfceeId;
     private final String mEntry;
+    private final String mRoutingType;
 
-    public Entry(String entry, byte type, byte nfceeId) {
+    public Entry(String entry, byte type, byte nfceeId, String routingType) {
         mEntry = entry;
         mType = type;
         mNfceeId = nfceeId;
+        mRoutingType = routingType;
     }
 
     public byte getType() {
@@ -44,6 +46,10 @@
         return mEntry;
     }
 
+    public String getRoutingType() {
+        return mRoutingType;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -53,6 +59,7 @@
         this.mEntry = in.readString();
         this.mNfceeId = in.readByte();
         this.mType = in.readByte();
+        this.mRoutingType = in.readString();
     }
 
     public static final @NonNull Parcelable.Creator<Entry> CREATOR =
@@ -73,5 +80,6 @@
         dest.writeString(mEntry);
         dest.writeByte(mNfceeId);
         dest.writeByte(mType);
+        dest.writeString(mRoutingType);
     }
 }
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index f78161e..fb11875 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -887,18 +887,22 @@
             switch (entry.getType()) {
                 case TYPE_TECHNOLOGY -> result.add(
                         new RoutingTableTechnologyEntry(entry.getNfceeId(),
-                                RoutingTableTechnologyEntry.techStringToInt(entry.getEntry()))
+                                RoutingTableTechnologyEntry.techStringToInt(entry.getEntry()),
+                                routeStringToInt(entry.getRoutingType()))
                 );
                 case TYPE_PROTOCOL -> result.add(
                         new RoutingTableProtocolEntry(entry.getNfceeId(),
-                                RoutingTableProtocolEntry.protocolStringToInt(entry.getEntry()))
+                                RoutingTableProtocolEntry.protocolStringToInt(entry.getEntry()),
+                                routeStringToInt(entry.getRoutingType()))
                 );
                 case TYPE_AID -> result.add(
-                        new RoutingTableAidEntry(entry.getNfceeId(), entry.getEntry())
+                        new RoutingTableAidEntry(entry.getNfceeId(), entry.getEntry(),
+                                routeStringToInt(entry.getRoutingType()))
                 );
                 case TYPE_SYSTEMCODE -> result.add(
                         new RoutingTableSystemCodeEntry(entry.getNfceeId(),
-                                entry.getEntry().getBytes(StandardCharsets.UTF_8))
+                                entry.getEntry().getBytes(StandardCharsets.UTF_8),
+                                routeStringToInt(entry.getRoutingType()))
                 );
             }
         }
diff --git a/nfc/java/android/nfc/NfcRoutingTableEntry.java b/nfc/java/android/nfc/NfcRoutingTableEntry.java
index c2cbbed..4153779 100644
--- a/nfc/java/android/nfc/NfcRoutingTableEntry.java
+++ b/nfc/java/android/nfc/NfcRoutingTableEntry.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.nfc.cardemulation.CardEmulation;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -35,6 +36,7 @@
 public abstract class NfcRoutingTableEntry {
     private final int mNfceeId;
     private final int mType;
+    private final int mRouteType;
 
     /**
      * AID routing table type.
@@ -67,9 +69,11 @@
     public @interface RoutingTableType {}
 
     /** @hide */
-    protected NfcRoutingTableEntry(int nfceeId, @RoutingTableType int type) {
+    protected NfcRoutingTableEntry(int nfceeId, @RoutingTableType int type,
+            @CardEmulation.ProtocolAndTechnologyRoute int routeType) {
         mNfceeId = nfceeId;
         mType = type;
+        mRouteType = routeType;
     }
 
     /**
@@ -88,4 +92,14 @@
     public int getType() {
         return mType;
     }
+
+    /**
+     * Get the route type of this entry.
+     * @return an integer defined in
+     * {@link android.nfc.cardemulation.CardEmulation.ProtocolAndTechnologyRoute}
+     */
+    @CardEmulation.ProtocolAndTechnologyRoute
+    public int getRouteType() {
+        return mRouteType;
+    }
 }
diff --git a/nfc/java/android/nfc/RoutingTableAidEntry.java b/nfc/java/android/nfc/RoutingTableAidEntry.java
index bf697d6..be94f9f 100644
--- a/nfc/java/android/nfc/RoutingTableAidEntry.java
+++ b/nfc/java/android/nfc/RoutingTableAidEntry.java
@@ -18,6 +18,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.nfc.cardemulation.CardEmulation;
 
 /**
  * Represents an Application ID (AID) entry in current routing table.
@@ -29,8 +30,9 @@
     private final String mValue;
 
     /** @hide */
-    public RoutingTableAidEntry(int nfceeId, String value) {
-        super(nfceeId, TYPE_AID);
+    public RoutingTableAidEntry(int nfceeId, String value,
+            @CardEmulation.ProtocolAndTechnologyRoute int routeType) {
+        super(nfceeId, TYPE_AID, routeType);
         this.mValue = value;
     }
 
diff --git a/nfc/java/android/nfc/RoutingTableProtocolEntry.java b/nfc/java/android/nfc/RoutingTableProtocolEntry.java
index 536de4d..a68d8c1 100644
--- a/nfc/java/android/nfc/RoutingTableProtocolEntry.java
+++ b/nfc/java/android/nfc/RoutingTableProtocolEntry.java
@@ -18,6 +18,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.nfc.cardemulation.CardEmulation;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -96,8 +97,9 @@
     private final @ProtocolValue int mValue;
 
     /** @hide */
-    public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value) {
-        super(nfceeId, TYPE_PROTOCOL);
+    public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value,
+            @CardEmulation.ProtocolAndTechnologyRoute int routeType) {
+        super(nfceeId, TYPE_PROTOCOL, routeType);
         this.mValue = value;
     }
 
diff --git a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
index f61892d..06cc0a5 100644
--- a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
+++ b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java
@@ -18,6 +18,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.nfc.cardemulation.CardEmulation;
 
 /**
  * Represents a system code entry in current routing table, where system codes are two-byte values
@@ -31,8 +32,9 @@
     private final byte[] mValue;
 
     /** @hide */
-    public RoutingTableSystemCodeEntry(int nfceeId, byte[] value) {
-        super(nfceeId, TYPE_SYSTEM_CODE);
+    public RoutingTableSystemCodeEntry(int nfceeId, byte[] value,
+            @CardEmulation.ProtocolAndTechnologyRoute int routeType) {
+        super(nfceeId, TYPE_SYSTEM_CODE, routeType);
         this.mValue = value;
     }
 
diff --git a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
index 2dbc942..86239ce7 100644
--- a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
+++ b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java
@@ -18,6 +18,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.nfc.cardemulation.CardEmulation;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -77,8 +78,9 @@
     private final @TechnologyValue int mValue;
 
     /** @hide */
-    public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value) {
-        super(nfceeId, TYPE_TECHNOLOGY);
+    public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value,
+            @CardEmulation.ProtocolAndTechnologyRoute int routeType) {
+        super(nfceeId, TYPE_TECHNOLOGY, routeType);
         this.mValue = value;
     }
 
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 4f601f0..db1f6a2 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -107,7 +107,7 @@
  *     &lt;intent-filter&gt;
  *         &lt;action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/&gt;
  *     &lt;/intent-filter&gt;
- *     &lt;meta-data android:name="android.nfc.cardemulation.host_apdu_ervice" android:resource="@xml/apduservice"/&gt;
+ *     &lt;meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice"/&gt;
  * &lt;/service&gt;</pre>
  *
  * This meta-data tag points to an apduservice.xml file.
diff --git a/nfc/java/android/nfc/cardemulation/OffHostApduService.java b/nfc/java/android/nfc/cardemulation/OffHostApduService.java
index 2286e84..8d8a172 100644
--- a/nfc/java/android/nfc/cardemulation/OffHostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/OffHostApduService.java
@@ -96,7 +96,7 @@
  *     &lt;intent-filter&gt;
  *         &lt;action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/&gt;
  *     &lt;/intent-filter&gt;
- *     &lt;meta-data android:name="android.nfc.cardemulation.off_host_apdu_ervice" android:resource="@xml/apduservice"/&gt;
+ *     &lt;meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/apduservice"/&gt;
  * &lt;/service&gt;</pre>
  *
  * This meta-data tag points to an apduservice.xml file.
diff --git a/nfc/tests/src/android/nfc/NdefRecordTest.java b/nfc/tests/src/android/nfc/NdefRecordTest.java
new file mode 100644
index 0000000..044c674
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NdefRecordTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NdefRecordTest {
+
+    @Test
+    public void testNdefRecordConstructor() throws FormatException {
+        NdefRecord applicationRecord = NdefRecord
+                .createApplicationRecord("com.android.test");
+        NdefRecord ndefRecord = new NdefRecord(applicationRecord.toByteArray());
+        assertThat(ndefRecord).isNotNull();
+        assertThat(ndefRecord.toByteArray().length).isGreaterThan(0);
+        assertThat(ndefRecord.getType()).isEqualTo("android.com:pkg".getBytes());
+        assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes());
+    }
+
+    @Test
+    public void testCreateExternal() {
+        NdefRecord ndefRecord = NdefRecord.createExternal("test",
+                "android.com:pkg", "com.android.test".getBytes());
+        assertThat(ndefRecord).isNotNull();
+        assertThat(ndefRecord.getType()).isEqualTo("test:android.com:pkg".getBytes());
+        assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes());
+    }
+
+    @Test
+    public void testCreateUri() {
+        NdefRecord ndefRecord = NdefRecord.createUri("http://www.example.com");
+        assertThat(ndefRecord).isNotNull();
+        assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN);
+        assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_URI);
+    }
+
+    @Test
+    public void testCreateMime() {
+        NdefRecord ndefRecord = NdefRecord.createMime("text/plain", "example".getBytes());
+        assertThat(ndefRecord).isNotNull();
+        assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_MIME_MEDIA);
+    }
+
+    @Test
+    public void testCreateTextRecord() {
+        String languageCode = Locale.getDefault().getLanguage();
+        NdefRecord ndefRecord = NdefRecord.createTextRecord(languageCode, "testdata");
+        assertThat(ndefRecord).isNotNull();
+        assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN);
+        assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_TEXT);
+    }
+
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 011ffbc..c0e61ee 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -178,11 +178,6 @@
     private static final String APEX_DIR = "/apex";
     private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
 
-    private static final String STORAGE_MIGRATION_FLAG =
-            "core_experiments_team_internal/com.android.providers.settings.storage_test_mission_1";
-    private static final String STORAGE_MIGRATION_MARKER_FILE =
-            "/metadata/aconfig_test_missions/mission_1";
-
     /**
      * This tag is applied to all aconfig default value-loaded flags.
      */
@@ -1753,32 +1748,6 @@
                     }
                 }
 
-                if (isConfigSettingsKey(mKey) && name != null
-                        && name.equals(STORAGE_MIGRATION_FLAG)) {
-                    if (value.equals("true")) {
-                        Path path = Paths.get(STORAGE_MIGRATION_MARKER_FILE);
-                        if (!Files.exists(path)) {
-                            Files.createFile(path);
-                        }
-
-                        Set<PosixFilePermission> perms =
-                                Files.readAttributes(path, PosixFileAttributes.class).permissions();
-                        perms.add(PosixFilePermission.OWNER_WRITE);
-                        perms.add(PosixFilePermission.OWNER_READ);
-                        perms.add(PosixFilePermission.GROUP_READ);
-                        perms.add(PosixFilePermission.OTHERS_READ);
-                        try {
-                            Files.setPosixFilePermissions(path, perms);
-                        } catch (Exception e) {
-                            Slog.e(LOG_TAG, "failed to set permissions on migration marker", e);
-                        }
-                    } else {
-                        java.nio.file.Path path = Paths.get(STORAGE_MIGRATION_MARKER_FILE);
-                        if (Files.exists(path)) {
-                            Files.delete(path);
-                        }
-                    }
-                }
                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
                         fromSystem, Long.valueOf(id), isPreservedInRestore));
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7392ad8..e43d771 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -65,7 +65,6 @@
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
-    <uses-permission android:name="android.permission.READ_DROPBOX_DATA" />
     <uses-permission android:name="android.permission.READ_LOGS" />
     <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
     <uses-permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index 785d5a8..0b98867 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -18,6 +18,7 @@
 
 import android.hardware.display.displayManager
 import android.media.projection.MediaProjectionInfo
+import android.media.projection.StopReason
 import android.os.Binder
 import android.os.Handler
 import android.os.UserHandle
@@ -306,8 +307,9 @@
     @Test
     fun stopProjecting_invokesManager() =
         testScope.runTest {
-            repo.stopProjecting()
+            repo.stopProjecting(StopReason.STOP_QS_TILE)
 
-            verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection()
+            verify(fakeMediaProjectionManager.mediaProjectionManager)
+                .stopActiveProjection(StopReason.STOP_QS_TILE)
         }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 22130f8..f2c7ed7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1870,7 +1870,9 @@
                     if (posture == DEVICE_POSTURE_OPENED) {
                         mLogger.d("Posture changed to open - attempting to request active"
                                 + " unlock and run face auth");
-                        getFaceAuthInteractor().onDeviceUnfolded();
+                        if (getFaceAuthInteractor() != null) {
+                            getFaceAuthInteractor().onDeviceUnfolded();
+                        }
                         requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE,
                                 false);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index 5704e80..f55eec8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -20,6 +20,7 @@
 import android.hardware.display.DisplayManager
 import android.media.projection.MediaProjectionInfo
 import android.media.projection.MediaProjectionManager
+import android.media.projection.StopReason
 import android.os.Handler
 import android.view.ContentRecordingSession
 import android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY
@@ -71,7 +72,7 @@
         }
     }
 
-    override suspend fun stopProjecting() {
+    override suspend fun stopProjecting(@StopReason stopReason: Int) {
         withContext(backgroundDispatcher) {
             logger.log(
                 TAG,
@@ -79,7 +80,7 @@
                 {},
                 { "Requesting MediaProjectionManager#stopActiveProjection" },
             )
-            mediaProjectionManager.stopActiveProjection()
+            mediaProjectionManager.stopActiveProjection(stopReason)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
index 50182d7..a01d8c2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.mediaprojection.data.repository
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.media.projection.StopReason
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import kotlinx.coroutines.flow.Flow
 
@@ -27,7 +28,7 @@
     suspend fun switchProjectedTask(task: RunningTaskInfo)
 
     /** Stops the currently active projection. */
-    suspend fun stopProjecting()
+    suspend fun stopProjecting(@StopReason stopReason: Int)
 
     /** Represents the current [MediaProjectionState]. */
     val mediaProjectionState: Flow<MediaProjectionState>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index c5f78d2..ae616a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
 
 import android.content.pm.PackageManager
+import android.media.projection.StopReason
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
@@ -83,7 +84,7 @@
 
     /** Stops the currently active projection. */
     fun stopProjecting() {
-        scope.launch { mediaProjectionRepository.stopProjecting() }
+        scope.launch { mediaProjectionRepository.stopProjecting(StopReason.STOP_PRIVACY_CHIP) }
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index a115baa..52f80fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -24,6 +24,7 @@
 import android.media.MediaRouter.RouteInfo;
 import android.media.projection.MediaProjectionInfo;
 import android.media.projection.MediaProjectionManager;
+import android.media.projection.StopReason;
 import android.os.Handler;
 import android.util.ArrayMap;
 
@@ -190,7 +191,7 @@
         if (isProjection) {
             final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag();
             if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
-                mProjectionManager.stopActiveProjection();
+                mProjectionManager.stopActiveProjection(StopReason.STOP_QS_TILE);
             } else {
                 mLogger.logStopCastingNoProjection(projection);
             }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
index d631f92..e6256a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.mediaprojection.data.repository
 
 import android.app.ActivityManager
+import android.media.projection.StopReason
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import kotlinx.coroutines.flow.MutableStateFlow
 
@@ -28,7 +29,7 @@
 
     var stopProjectingInvoked = false
 
-    override suspend fun stopProjecting() {
+    override suspend fun stopProjecting(@StopReason stopReason: Int) {
         stopProjectingInvoked = true
     }
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index de3c5f2..9644a52 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -63,8 +63,6 @@
  * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
  */
 public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
-    public static final String TAG = "Ravenwood";
-
     /** Scope of a hook. */
     public enum Scope {
         Class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 239c806..9eff20a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -45,7 +45,7 @@
 import java.util.function.Supplier;
 
 public class RavenwoodContext extends RavenwoodBaseContext {
-    private static final String TAG = "Ravenwood";
+    private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
 
     private final Object mLock = new Object();
     private final String mPackageName;
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
index 77275c4..3cb2c67 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
@@ -27,8 +27,6 @@
  * Calculates which tests need to be executed on Ravenwood.
  */
 public class RavenwoodEnablementChecker {
-    private static final String TAG = "RavenwoodDisablementChecker";
-
     private RavenwoodEnablementChecker() {
     }
 
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index 6dfcf4ce..a5d0bfd 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -40,7 +40,7 @@
  * All members must be called from the runner's main thread.
  */
 public final class RavenwoodRunnerState {
-    private static final String TAG = "RavenwoodRunnerState";
+    private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
     private static final String RAVENWOOD_RULE_ERROR =
             "RavenwoodRule(s) are not executed in the correct order";
 
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 172cec3..930914f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -96,7 +96,7 @@
  * Responsible for initializing and the environment.
  */
 public class RavenwoodRuntimeEnvironmentController {
-    private static final String TAG = "RavenwoodRuntimeEnvironmentController";
+    private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
 
     private RavenwoodRuntimeEnvironmentController() {
     }
@@ -105,6 +105,9 @@
     private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
     private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
 
+    private static final String ANDROID_LOG_TAGS = "ANDROID_LOG_TAGS";
+    private static final String RAVENWOOD_ANDROID_LOG_TAGS = "RAVENWOOD_" + ANDROID_LOG_TAGS;
+
     /**
      * When enabled, attempt to dump all thread stacks just before we hit the
      * overall Tradefed timeout, to aid in debugging deadlocks.
@@ -232,20 +235,22 @@
         // Make sure libravenwood_runtime is loaded.
         System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
 
+        Log_ravenwood.setLogLevels(getLogTags());
         Log_ravenwood.onRavenwoodRuntimeNativeReady();
 
         // Do the basic set up for the android sysprops.
         RavenwoodSystemProperties.initialize();
 
+        // Enable all log levels for native logging, until we'll have a way to change the native
+        // side log level at runtime.
         // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
         // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
-        if (RAVENWOOD_VERBOSE_LOGGING) {
-            RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
-            try {
-                Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
-            } catch (ErrnoException e) {
-                throw new RuntimeException(e);
-            }
+        // This would also prevent libbase from crashing the process (b/381112373) because
+        // the string format it accepts is very limited.
+        try {
+            Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
         }
 
         // Make sure libandroid_runtime is loaded.
@@ -333,6 +338,18 @@
         initializeCompatIds();
     }
 
+    /**
+     * Get log tags from environmental variable.
+     */
+    @Nullable
+    private static String getLogTags() {
+        var logTags = System.getenv(RAVENWOOD_ANDROID_LOG_TAGS);
+        if (logTags == null) {
+            logTags = System.getenv(ANDROID_LOG_TAGS);
+        }
+        return logTags;
+    }
+
     private static void loadRavenwoodProperties() {
         var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");
 
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 99b38ed..fac0791 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -34,7 +34,7 @@
  * A class to manage the core default system properties of the Ravenwood environment.
  */
 public class RavenwoodSystemProperties {
-    private static final String TAG = "RavenwoodSystemProperties";
+    private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
 
     /** We pull in properties from this file. */
     private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index 7870585..c8b10d2 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -41,7 +41,7 @@
  * `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_latest.csv`.
  */
 public class RavenwoodTestStats {
-    private static final String TAG = "RavenwoodTestStats";
+    private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
     private static final String HEADER = "Module,Class,OuterClass,Passed,Failed,Skipped";
 
     private static RavenwoodTestStats sInstance;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
index 31a1416..f3688d6 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
@@ -35,7 +35,7 @@
 import org.junit.runners.model.TestClass;
 
 abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable, Orderable {
-    private static final String TAG = "Ravenwood";
+    public static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
 
     boolean mRealRunnerTakesRunnerBuilder = false;
 
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index e49d3d9..d8cde0e 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -41,7 +41,7 @@
  */
 @Deprecated
 public final class RavenwoodRule implements TestRule {
-    private static final String TAG = "RavenwoodRule";
+    private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
 
     static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
 
@@ -193,6 +193,12 @@
         return IS_ON_RAVENWOOD;
     }
 
+    private static void ensureOnRavenwood(String featureName) {
+        if (!IS_ON_RAVENWOOD) {
+            throw new RuntimeException(featureName + " is only supported on Ravenwood.");
+        }
+    }
+
     /**
      * @deprecated Use
      * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
@@ -242,6 +248,40 @@
         return System.currentTimeMillis();
     }
 
+    /**
+     * Equivalent to setting the ANDROID_LOG_TAGS environmental variable.
+     *
+     * See https://developer.android.com/tools/logcat#filteringOutput for the string format.
+     *
+     * NOTE: this works only on Ravenwood.
+     */
+    public static void setAndroidLogTags(@Nullable String androidLogTags) {
+        ensureOnRavenwood("RavenwoodRule.setAndroidLogTags()");
+        try {
+            Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood");
+            var setter = logRavenwoodClazz.getMethod("setLogLevels", String.class);
+            setter.invoke(null, androidLogTags);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Set a log level for a given tag. Pass NULL to {@code tag} to change the default level.
+     *
+     * NOTE: this works only on Ravenwood.
+     */
+    public static void setLogLevel(@Nullable String tag, int level) {
+        ensureOnRavenwood("RavenwoodRule.setLogLevel()");
+        try {
+            Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood");
+            var setter = logRavenwoodClazz.getMethod("setLogLevel", String.class, int.class);
+            setter.invoke(null, tag, level);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     // Below are internal to ravenwood. Don't use them from normal tests...
 
     public static class RavenwoodPrivate {
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index b4b75178..8d9bcd5 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -31,8 +31,6 @@
  * This is only used when a real device-side test has Ravenizer enabled.
  */
 public class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
-    private static final String TAG = "Ravenwood";
-
     private static class NopRule implements TestRule {
         @Override
         public Statement apply(Statement base, Description description) {
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 2a04d44..a967a3f 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -33,7 +33,7 @@
 import java.util.function.Supplier;
 
 public class RavenwoodCommonUtils {
-    private static final String TAG = "RavenwoodCommonUtils";
+    public static final String TAG = "Ravenwood";
 
     private RavenwoodCommonUtils() {
     }
diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
index 7b26fe5..7ab9cda 100644
--- a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
+++ b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
@@ -15,8 +15,10 @@
  */
 package android.util;
 
+import android.annotation.Nullable;
 import android.util.Log.Level;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.RuntimeInit;
 import com.android.ravenwood.RavenwoodRuntimeNative;
 import com.android.ravenwood.common.RavenwoodCommonUtils;
@@ -24,7 +26,9 @@
 import java.io.PrintStream;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * Ravenwood "native substitution" class for {@link android.util.Log}.
@@ -35,16 +39,100 @@
  */
 public class Log_ravenwood {
 
-    public static final SimpleDateFormat sTimestampFormat =
+    private static final SimpleDateFormat sTimestampFormat =
             new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
 
-    public static boolean isLoggable(String tag, @Level int level) {
-        return true;
+    private static final Object sLock = new Object();
+
+    @GuardedBy("sLock")
+    private static int sDefaultLogLevel;
+
+    @GuardedBy("sLock")
+    private static final Map<String, Integer> sTagLogLevels = new HashMap<>();
+
+    /**
+     * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setAndroidLogTags(String)}
+     * via reflections.
+     */
+    public static void setLogLevels(String androidLogTags) {
+        var map = parseLogLevels(androidLogTags);
+
+        synchronized (sLock) {
+            sTagLogLevels.clear();
+            sTagLogLevels.putAll(map);
+
+            var def = map.get("*");
+            sDefaultLogLevel = def != null ? def : Log.VERBOSE;
+        }
+    }
+
+    private static Map<String, Integer> parseLogLevels(String androidLogTags) {
+        final Map<String, Integer> ret = new HashMap<>();
+
+        if (androidLogTags == null) {
+            return ret;
+        }
+
+        String[] tagPairs = androidLogTags.trim().split("\\s+");
+        for (String tagPair : tagPairs) {
+            String[] parts = tagPair.split(":");
+            if (parts.length == 2) {
+                String tag = parts[0];
+                try {
+                    int priority = switch (parts[1].toUpperCase(Locale.ROOT)) {
+                        case "V": yield Log.VERBOSE;
+                        case "D": yield Log.DEBUG;
+                        case "I": yield Log.INFO;
+                        case "W": yield Log.WARN;
+                        case "E": yield Log.ERROR;
+                        case "F": yield Log.ERROR + 1; // Not used in the java side.
+                        case "S": yield Integer.MAX_VALUE; // Silent
+                        default: throw new IllegalArgumentException(
+                                "Invalid priority level for tag: " + tag);
+                    };
+
+                    ret.put(tag, priority);
+                } catch (IllegalArgumentException e) {
+                    System.err.println(e.getMessage());
+                }
+            } else {
+                System.err.println("Invalid tag format: " + tagPair);
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setLogLevel(String, int)}
+     * via reflections. Pass NULL to {@code tag} to set the default level.
+     */
+    public static void setLogLevel(@Nullable String tag, int level) {
+        synchronized (sLock) {
+            if (tag == null) {
+                sDefaultLogLevel = level;
+            } else {
+                sTagLogLevels.put(tag, level);
+            }
+        }
+    }
+
+    /**
+     * Replaces {@link Log#isLoggable}.
+     */
+    public static boolean isLoggable(String tag, @Level int priority) {
+        synchronized (sLock) {
+            var threshold = sTagLogLevels.get(tag);
+            if (threshold == null) {
+                threshold = sDefaultLogLevel;
+            }
+            return priority >= threshold;
+        }
     }
 
     public static int println_native(int bufID, int priority, String tag, String msg) {
-        if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) {
-            return msg.length(); // No verbose logging.
+        if (!isLoggable(tag, priority)) {
+            return msg.length();
         }
 
         final String prio;
diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py
new file mode 100755
index 0000000..bdc1de0
--- /dev/null
+++ b/ravenwood/scripts/extract-last-soong-commands.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 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.
+
+
+# This script extracts all the commands executed in the last soong run,
+# and write them into a script file, and print the filename.
+#
+# All the commands are commented out. Uncomment what you want to execute as
+# needed before running it.
+
+import datetime
+import gzip
+import os
+import re
+import shlex
+import sys
+
+re_command = re.compile(r''' ^\[.*?\]  \s*  (.*) ''', re.X)
+
+HEADER = r'''#!/bin/bash
+
+set -e # Stop on a failed command
+
+cd "${ANDROID_BUILD_TOP:?}"
+
+'''
+
+OUT_SCRIPT_DIR = "/tmp/"
+OUT_SCRIPT_FORMAT = "soong-rerun-%Y-%m-%d_%H-%M-%S.sh"
+
+def main(args):
+    log = os.environ["ANDROID_BUILD_TOP"] + "/out/verbose.log.gz"
+    outdir = "/tmp/"
+    outfile = outdir + datetime.datetime.now().strftime(OUT_SCRIPT_FORMAT)
+
+    with open(outfile, "w") as out:
+        out.write(HEADER)
+
+        with gzip.open(log) as f:
+            for line in f:
+                s = line.decode("utf-8")
+
+                if s.startswith("verbose"):
+                    continue
+                if re.match('^\[.*bootstrap blueprint', s):
+                    continue
+
+                s = s.rstrip()
+
+                m = re_command.search(s)
+                if m:
+                    command = m.groups()[0]
+
+                    out.write('#========\n')
+
+                    # Show the full command line before executing it.
+                    out.write('#echo ' + shlex.quote(command) + '\n')
+                    out.write('\n')
+
+                    # Execute the command.
+                    out.write('#' + command + '\n')
+
+                    out.write('\n')
+
+                    continue
+
+                if s.startswith("FAILED:"):
+                    break
+
+    os.chmod(outfile, 0o755)
+    print(outfile)
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java
new file mode 100644
index 0000000..74c1f3f6
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 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.ravenwoodtest.coretest;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import org.junit.Test;
+
+public class RavenwoodLogLevelTest {
+    /**
+     * Assert that the `priority` is loggable, but one level below is not.
+     */
+    private void assertBarelyLoggable(String tag, int priority) {
+        assertEquals(true, Log.isLoggable(tag, priority));
+        assertEquals(false, Log.isLoggable(tag, priority - 1));
+    }
+
+    @Test
+    public void testDefaultLogTags() {
+        RavenwoodRule.setAndroidLogTags(null);
+
+        // Info should always be loggable.
+        assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+        assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+        assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+        assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+    }
+
+    @Test
+    public void testAllVerbose() {
+        RavenwoodRule.setAndroidLogTags("*:V");
+
+        assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+        assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+        assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+        assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+    }
+
+    @Test
+    public void testAllSilent() {
+        RavenwoodRule.setAndroidLogTags("*:S");
+
+        assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT));
+        assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT));
+    }
+
+    @Test
+    public void testComplex() {
+        RavenwoodRule.setAndroidLogTags("TAG1:W TAG2:D *:I");
+
+        assertBarelyLoggable("TAG1", Log.WARN);
+        assertBarelyLoggable("TAG2", Log.DEBUG);
+        assertBarelyLoggable("TAG3", Log.INFO);
+    }
+
+    @Test
+    public void testAllVerbose_setLogLevel() {
+        RavenwoodRule.setAndroidLogTags(null);
+        RavenwoodRule.setLogLevel(null, Log.VERBOSE);
+
+        assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+        assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+        assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+        assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+    }
+
+    @Test
+    public void testAllSilent_setLogLevel() {
+        RavenwoodRule.setAndroidLogTags(null);
+        RavenwoodRule.setLogLevel(null, Log.ASSERT + 1);
+
+        assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT));
+        assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT));
+    }
+
+    @Test
+    public void testComplex_setLogLevel() {
+        RavenwoodRule.setAndroidLogTags(null);
+        RavenwoodRule.setLogLevel(null, Log.INFO);
+        RavenwoodRule.setLogLevel("TAG1", Log.WARN);
+        RavenwoodRule.setLogLevel("TAG2", Log.DEBUG);
+
+        assertBarelyLoggable("TAG1", Log.WARN);
+        assertBarelyLoggable("TAG2", Log.DEBUG);
+        assertBarelyLoggable("TAG3", Log.INFO);
+    }
+}
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
index 08f53977..83c3151 100644
--- a/ravenwood/texts/ravenwood-common-policies.txt
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -14,7 +14,7 @@
 
 # Support APIs not available in standard JRE
 class java.io.FileDescriptor keep
-    method getInt$ ()I @com.android.ravenwood.RavenwoodJdkPatch.getInt$
-    method setInt$ (I)V @com.android.ravenwood.RavenwoodJdkPatch.setInt$
+    method getInt$ @com.android.ravenwood.RavenwoodJdkPatch.getInt$
+    method setInt$ @com.android.ravenwood.RavenwoodJdkPatch.setInt$
 class java.util.LinkedHashMap keep
-    method eldest ()Ljava/util/Map$Entry; @com.android.ravenwood.RavenwoodJdkPatch.eldest
+    method eldest @com.android.ravenwood.RavenwoodJdkPatch.eldest
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index 59fa464..fc885d6 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -66,6 +66,9 @@
         methodName: String,
         descriptor: String
     ) {
+        if (descriptor == "*") {
+            return
+        }
         if (classes.findMethod(className, methodName, descriptor) == null) {
             log.w("Unknown method $className.$methodName$descriptor")
         }
@@ -92,7 +95,8 @@
             descriptor: String,
             ): FilterPolicyWithReason {
         return mPolicies[getMethodKey(className, methodName, descriptor)]
-                ?: super.getPolicyForMethod(className, methodName, descriptor)
+            ?: mPolicies[getMethodKey(className, methodName, "*")]
+            ?: super.getPolicyForMethod(className, methodName, descriptor)
     }
 
     fun setPolicyForMethod(
@@ -107,7 +111,8 @@
 
     override fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
         return mRenames[getMethodKey(className, methodName, descriptor)]
-                ?: super.getRenameTo(className, methodName, descriptor)
+            ?: mRenames[getMethodKey(className, methodName, "*")]
+            ?: super.getRenameTo(className, methodName, descriptor)
     }
 
     fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index caf80eb..7462a8c 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -303,12 +303,21 @@
     }
 
     private fun parseMethod(fields: Array<String>) {
-        if (fields.size < 4) {
-            throw ParseException("Method ('m') expects 3 fields.")
+        if (fields.size < 3 || fields.size > 4) {
+            throw ParseException("Method ('m') expects 3 or 4 fields.")
         }
         val name = fields[1]
-        val signature = fields[2]
-        val policy = parsePolicy(fields[3])
+        val signature: String
+        val policyStr: String
+        if (fields.size <= 3) {
+            signature = "*"
+            policyStr = fields[2]
+        } else {
+            signature = fields[2]
+            policyStr = fields[3]
+        }
+
+        val policy = parsePolicy(policyStr)
 
         if (!policy.isUsableWithMethods) {
             throw ParseException("Method can't have policy '$policy'")
@@ -321,7 +330,7 @@
             policy.withReason(FILTER_REASON)
         )
         if (policy == FilterPolicy.Substitute) {
-            val fromName = fields[3].substring(1)
+            val fromName = policyStr.substring(1)
 
             if (fromName == name) {
                 throw ParseException(
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
index d45f414..a3f934c 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
@@ -48,10 +48,11 @@
         // Maybe use 'Tri' if we end up having too many replacements.
         spec.forEach {
             if (className == it.fromClass &&
-                methodName == it.fromMethod &&
-                descriptor == it.fromDescriptor
+                methodName == it.fromMethod
                 ) {
-                return MethodReplaceTarget(it.toClass, it.toMethod)
+                if (it.fromDescriptor == "*" || descriptor == it.fromDescriptor) {
+                    return MethodReplaceTarget(it.toClass, it.toMethod)
+                }
             }
         }
         return null
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 3c138d2..2f35d35 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -3,11 +3,11 @@
   # field remove	remove # Implicitly remove
   method <init>	()V	            keep
   method addOne	(I)I	        keep
-  method addOneInner	(I)I	keep
+  method addOneInner	keep
   method toBeRemoved	(Ljava/lang/String;)V	remove
   method addTwo	(I)I	        @addTwo_host
   # method addTwo_host	(I)I	# used as a substitute
-  method nativeAddThree	(I)I	@addThree_host
+  method nativeAddThree	        @addThree_host
   # method addThree_host	(I)I	# used as a substitute
   method unsupportedMethod	()Ljava/lang/String;	throw
   method visibleButUsesUnsupportedMethod	()Ljava/lang/String;	keep
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 43774bbc..b0dae6a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -90,6 +90,7 @@
      */
     public static final int RESOLVE_NON_RESOLVER_ONLY = 0x00000002;
 
+    @Deprecated
     @IntDef(value = {
             INTEGRITY_VERIFICATION_ALLOW,
             INTEGRITY_VERIFICATION_REJECT,
@@ -97,18 +98,10 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface IntegrityVerificationResult {}
 
-    /**
-     * Used as the {@code verificationCode} argument for
-     * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the
-     * integrity component allows the install to proceed.
-     */
+    @Deprecated
     public static final int INTEGRITY_VERIFICATION_ALLOW = 1;
 
-    /**
-     * Used as the {@code verificationCode} argument for
-     * {@link PackageManagerInternal#setIntegrityVerificationResult(int, int)} to indicate that the
-     * integrity component does not allow install to proceed.
-     */
+    @Deprecated
     public static final int INTEGRITY_VERIFICATION_REJECT = 0;
 
     /**
@@ -1131,17 +1124,13 @@
     public abstract boolean isPermissionUpgradeNeeded(@UserIdInt int userId);
 
     /**
-     * Allows the integrity component to respond to the
-     * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
-     * broadcast} to respond to the package manager. The response must include
-     * the {@code verificationCode} which is one of
-     * {@link #INTEGRITY_VERIFICATION_ALLOW} and {@link #INTEGRITY_VERIFICATION_REJECT}.
+     * Used to allow the integrity component to respond to the
+     * ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
+     * broadcast to respond to the package manager.
      *
-     * @param verificationId pending package identifier as passed via the
-     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
-     * @param verificationResult either {@link #INTEGRITY_VERIFICATION_ALLOW}
-     *            or {@link #INTEGRITY_VERIFICATION_REJECT}.
+     * Deprecated.
      */
+    @Deprecated
     public abstract void setIntegrityVerificationResult(int verificationId,
             @IntegrityVerificationResult int verificationResult);
 
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index a132876b..0914b7e 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -93,29 +93,6 @@
         mContext = context;
         mPackageManagerInternal = packageManagerInternal;
         mHandler = handler;
-
-        IntentFilter integrityVerificationFilter = new IntentFilter();
-        integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
-        try {
-            integrityVerificationFilter.addDataType(PACKAGE_MIME_TYPE);
-        } catch (IntentFilter.MalformedMimeTypeException e) {
-            throw new RuntimeException("Mime type malformed: should never happen.", e);
-        }
-
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        if (!ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION.equals(
-                                intent.getAction())) {
-                            return;
-                        }
-                        mHandler.post(() -> handleIntegrityVerification(intent));
-                    }
-                },
-                integrityVerificationFilter,
-                /* broadcastPermission= */ null,
-                mHandler);
     }
 
     @Override
@@ -157,10 +134,4 @@
     public List<String> getWhitelistedRuleProviders() {
         return Collections.emptyList();
     }
-
-    private void handleIntegrityVerification(Intent intent) {
-        int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
-        mPackageManagerInternal.setIntegrityVerificationResult(
-                verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-    }
 }
diff --git a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
index 6c74cba..05fe781 100644
--- a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
+++ b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
@@ -30,7 +30,8 @@
             int hostUid,
             int targetUid,
             int timeSinceLastActive,
-            int creationSource) {
+            int creationSource,
+            int stopSource) {
         FrameworkStatsLog.write(
                 code,
                 sessionId,
@@ -39,7 +40,8 @@
                 hostUid,
                 targetUid,
                 timeSinceLastActive,
-                creationSource);
+                creationSource,
+                stopSource);
     }
 
     /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionTargetChanged atom. */
@@ -49,13 +51,23 @@
             int targetType,
             int hostUid,
             int targetUid,
-            int windowingMode) {
+            int windowingMode,
+            int width,
+            int height,
+            int centerX,
+            int centerY,
+            int targetChangeType) {
         FrameworkStatsLog.write(
                 code,
                 sessionId,
                 targetType,
                 hostUid,
                 targetUid,
-                windowingMode);
+                windowingMode,
+                width,
+                height,
+                centerX,
+                centerY,
+                targetChangeType);
     }
 }
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index e7e519e..db4cdb3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -55,6 +55,7 @@
 import android.content.pm.PackageManager.ApplicationInfoFlags;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.media.MediaRouter;
 import android.media.projection.IMediaProjection;
@@ -64,6 +65,7 @@
 import android.media.projection.MediaProjectionInfo;
 import android.media.projection.MediaProjectionManager;
 import android.media.projection.ReviewGrantedConsentResult;
+import android.media.projection.StopReason;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -229,7 +231,7 @@
             if (mProjectionGrant != null && !canCaptureKeyguard()) {
                 Slog.d(TAG, "Content Recording: Stopped MediaProjection"
                         + " due to keyguard lock");
-                mProjectionGrant.stop();
+                mProjectionGrant.stop(StopReason.STOP_DEVICE_LOCKED);
             }
         }
     }
@@ -306,7 +308,7 @@
         synchronized (mLock) {
             if (mProjectionGrant != null) {
                 Slog.d(TAG, "Content Recording: Stopped MediaProjection due to user switching");
-                mProjectionGrant.stop();
+                mProjectionGrant.stop(StopReason.STOP_USER_SWITCH);
             }
         }
     }
@@ -344,7 +346,7 @@
             Slog.d(TAG,
                     "Content Recording: Stopped MediaProjection due to foreground service change");
             if (mProjectionGrant != null) {
-                mProjectionGrant.stop();
+                mProjectionGrant.stop(StopReason.STOP_FOREGROUND_SERVICE_CHANGE);
             }
         }
     }
@@ -353,7 +355,7 @@
         if (mProjectionGrant != null) {
             Slog.d(TAG, "Content Recording: Stopped MediaProjection to start new "
                     + "incoming projection");
-            mProjectionGrant.stop();
+            mProjectionGrant.stop(StopReason.STOP_NEW_PROJECTION);
         }
         if (mMediaRouteInfo != null) {
             mMediaRouter.getFallbackRoute().select();
@@ -363,7 +365,10 @@
         dispatchStart(projection);
     }
 
-    private void stopProjectionLocked(final MediaProjection projection) {
+    private void stopProjectionLocked(
+            final MediaProjection projection,
+            @StopReason int stopReason
+    ) {
         Slog.d(TAG, "Content Recording: Stopped active MediaProjection and "
                 + "dispatching stop to callbacks");
         ContentRecordingSession session = projection.mSession;
@@ -371,7 +376,7 @@
                 session != null
                         ? session.getTargetUid()
                         : ContentRecordingSession.TARGET_UID_UNKNOWN;
-        mMediaProjectionMetricsLogger.logStopped(projection.uid, targetUid);
+        mMediaProjectionMetricsLogger.logStopped(projection.uid, targetUid, stopReason);
         mProjectionToken = null;
         mProjectionGrant = null;
         dispatchStop(projection);
@@ -452,7 +457,7 @@
                             + "ContentRecordingSession - id= "
                             + mProjectionGrant.getVirtualDisplayId() + "type=" + projectionType);
 
-                    mProjectionGrant.stop();
+                    mProjectionGrant.stop(StopReason.STOP_ERROR);
                 }
                 return false;
             }
@@ -566,6 +571,18 @@
         }
     }
 
+    @VisibleForTesting
+    void notifyCaptureBoundsChanged(int contentToRecord, int targetUid, Rect captureBounds) {
+        synchronized (mLock) {
+            if (mProjectionGrant == null) {
+                Slog.i(TAG, "Cannot log MediaProjectionTargetChanged atom due to null projection");
+            } else {
+                mMediaProjectionMetricsLogger.logChangedCaptureBounds(
+                        contentToRecord, mProjectionGrant.uid, targetUid, captureBounds);
+            }
+        }
+    }
+
     /**
      * Handles result of dialog shown from
      * {@link BinderService#buildReviewGrantedConsentIntentLocked()}.
@@ -606,7 +623,7 @@
                         Slog.w(TAG, "Content Recording: Stopped MediaProjection due to user "
                                 + "consent result of CANCEL - "
                                 + "id= " + mProjectionGrant.getVirtualDisplayId());
-                        mProjectionGrant.stop();
+                        mProjectionGrant.stop(StopReason.STOP_ERROR);
                     }
                     break;
                 case RECORD_CONTENT_DISPLAY:
@@ -820,14 +837,14 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
         @Override // Binder call
-        public void stopActiveProjection() {
+        public void stopActiveProjection(@StopReason int stopReason) {
             stopActiveProjection_enforcePermission();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     if (mProjectionGrant != null) {
                         Slog.d(TAG, "Content Recording: Stopping active projection");
-                        mProjectionGrant.stop();
+                        mProjectionGrant.stop(stopReason);
                     }
                 }
             } finally {
@@ -837,8 +854,9 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
         @Override // Binder call
-        public void notifyActiveProjectionCapturedContentResized(int width, int height) {
-            notifyActiveProjectionCapturedContentResized_enforcePermission();
+        public void notifyCaptureBoundsChanged(
+                int contentToRecord, int targetProcessUid, Rect newBounds) {
+            notifyCaptureBoundsChanged_enforcePermission();
             synchronized (mLock) {
                 if (!isCurrentProjection(mProjectionGrant)) {
                     return;
@@ -848,7 +866,13 @@
             try {
                 synchronized (mLock) {
                     if (mProjectionGrant != null && mCallbackDelegate != null) {
-                        mCallbackDelegate.dispatchResize(mProjectionGrant, width, height);
+                        // log metrics for the new bounds
+                        MediaProjectionManagerService.this.notifyCaptureBoundsChanged(
+                                contentToRecord, targetProcessUid, newBounds);
+
+                        // update clients of the new update bounds
+                        mCallbackDelegate.dispatchResize(
+                                mProjectionGrant, newBounds.width(), newBounds.height());
                     }
                 }
             } finally {
@@ -1173,7 +1197,7 @@
                         Slog.d(TAG, "Content Recording: MediaProjection stopped by Binder death - "
                                 + "id= " + mVirtualDisplayId);
                         mCallbackDelegate.remove(callback);
-                        stop();
+                        stop(StopReason.STOP_TARGET_REMOVED);
                     };
                     mToken.linkToDeath(mDeathEater, 0);
                 } catch (RemoteException e) {
@@ -1222,7 +1246,7 @@
         }
 
         @Override // Binder call
-        public void stop() {
+        public void stop(@StopReason int stopReason) {
             synchronized (mLock) {
                 if (!isCurrentProjection(asBinder())) {
                     Slog.w(TAG, "Attempted to stop inactive MediaProjection "
@@ -1251,7 +1275,7 @@
                 Slog.d(TAG, "Content Recording: handling stopping this projection token"
                         + " createTime= " + mCreateTimeMs
                         + " countStarts= " + mCountStarts);
-                stopProjectionLocked(this);
+                stopProjectionLocked(this, stopReason);
                 mToken.unlinkToDeath(mDeathEater, 0);
                 mToken = null;
                 unregisterCallback(mCallback);
@@ -1326,7 +1350,7 @@
                     Slog.v(TAG, "Reusing token: Throw exception due to invalid projection.");
                     // Tear down projection here; necessary to ensure (among other reasons) that
                     // stop is dispatched to client and cast icon disappears from status bar.
-                    mProjectionGrant.stop();
+                    mProjectionGrant.stop(StopReason.STOP_ERROR);
                     throw new SecurityException("Don't re-use the resultData to retrieve "
                             + "the same projection instance, and don't use a token that has "
                             + "timed out. Don't take multiple captures by invoking "
@@ -1343,7 +1367,7 @@
             notifyVirtualDisplayCreated_enforcePermission();
             if (mKeyguardManager.isKeyguardLocked() && !canCaptureKeyguard()) {
                 Slog.w(TAG, "Content Recording: Keyguard locked, aborting MediaProjection");
-                stop();
+                stop(StopReason.STOP_DEVICE_LOCKED);
                 return;
             }
             synchronized (mLock) {
@@ -1385,7 +1409,7 @@
                     if (mProjectionGrant != null) {
                         Slog.d(TAG, "Content Recording: Stopped MediaProjection due to "
                                 + "route type of REMOTE_DISPLAY not selected");
-                        mProjectionGrant.stop();
+                        mProjectionGrant.stop(StopReason.STOP_NEW_MEDIA_ROUTE);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index be2a25a..8e5c016 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -19,15 +19,34 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_BOUNDS;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_POSITION;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_WINDOWING_MODE;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
@@ -36,8 +55,12 @@
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
 
+import android.app.WindowConfiguration;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.media.projection.StopReason;
 import android.util.Log;
 import android.view.ContentRecordingSession.RecordContent;
 
@@ -59,8 +82,10 @@
     private final MediaProjectionSessionIdGenerator mSessionIdGenerator;
     private final MediaProjectionTimestampStore mTimestampStore;
 
-    private int mPreviousState =
-            FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+    private final Rect mPreviousTargetBounds = new Rect();
+    private int mPreviousTargetWindowingMode = WINDOWING_MODE_UNDEFINED;
+    private int mPreviousProjectionState =
+            MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
 
     MediaProjectionMetricsLogger(
             FrameworkStatsLogWrapper frameworkStatsLogWrapper,
@@ -113,7 +138,8 @@
                 hostUid,
                 TARGET_UID_UNKNOWN,
                 timeSinceLastActiveInSeconds,
-                sessionCreationSource);
+                sessionCreationSource,
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
     }
 
     /**
@@ -130,7 +156,8 @@
                 hostUid,
                 TARGET_UID_UNKNOWN,
                 TIME_SINCE_LAST_ACTIVE_UNKNOWN,
-                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN,
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
     }
 
     /**
@@ -141,13 +168,12 @@
     public void logProjectionPermissionRequestCancelled(int hostUid) {
         writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
-                FrameworkStatsLog
-                        .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
+                MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
                 hostUid,
                 TARGET_UID_UNKNOWN,
                 TIME_SINCE_LAST_ACTIVE_UNKNOWN,
-                FrameworkStatsLog
-                        .MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN,
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
     }
 
     /**
@@ -163,7 +189,8 @@
                 hostUid,
                 TARGET_UID_UNKNOWN,
                 TIME_SINCE_LAST_ACTIVE_UNKNOWN,
-                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN,
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
     }
 
     /**
@@ -180,7 +207,8 @@
                 hostUid,
                 targetUid,
                 TIME_SINCE_LAST_ACTIVE_UNKNOWN,
-                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN,
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
     }
 
     /**
@@ -196,14 +224,63 @@
      */
     public void logChangedWindowingMode(
             int contentToRecord, int hostUid, int targetUid, int windowingMode) {
-        Log.d(TAG, "logChangedWindowingMode");
+        Log.d(TAG, "logChangedWindowingMode: windowingMode= "
+                + WindowConfiguration.windowingModeToString(windowingMode));
+        Log.d(TAG, "targetChangeType= changeWindowingMode");
         writeTargetChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 contentToRecordToTargetType(contentToRecord),
                 hostUid,
                 targetUid,
-                windowingModeToTargetWindowingMode(windowingMode));
+                windowingModeToTargetWindowingMode(windowingMode),
+                mPreviousTargetBounds.width(),
+                mPreviousTargetBounds.height(),
+                mPreviousTargetBounds.centerX(),
+                mPreviousTargetBounds.centerY(),
+                MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_WINDOWING_MODE);
+        mPreviousTargetWindowingMode = windowingMode;
+    }
 
+    /**
+     * Logs that the bounds of projection's capture target has changed.
+     *
+     * @param contentToRecord ContentRecordingSession.RecordContent indicating whether it is a
+     *                        task capture or display capture - gets converted to the corresponding
+     *                        TargetType before being logged.
+     * @param hostUid UID of the package that initiates MediaProjection.
+     * @param targetUid UID of the package that is captured if selected.
+     * @param captureBounds Updated bounds of the captured region.
+     */
+    public void logChangedCaptureBounds(
+            int contentToRecord, int hostUid, int targetUid, Rect captureBounds) {
+        final Point capturePosition = new Point(captureBounds.centerX(), captureBounds.centerY());
+        Log.d(TAG, "logChangedCaptureBounds: captureBounds= " + captureBounds + " position= "
+                + capturePosition);
+
+        writeTargetChanged(
+                mSessionIdGenerator.getCurrentSessionId(),
+                contentToRecordToTargetType(contentToRecord),
+                hostUid,
+                targetUid,
+                mPreviousTargetWindowingMode,
+                captureBounds.width(),
+                captureBounds.height(),
+                captureBounds.centerX(),
+                captureBounds.centerY(),
+                captureBoundsToTargetChangeType(captureBounds));
+        mPreviousTargetBounds.set(captureBounds);
+    }
+
+    private int captureBoundsToTargetChangeType(Rect captureBounds) {
+        final boolean hasChangedSize = captureBounds.width() != mPreviousTargetBounds.width()
+                && captureBounds.height() != mPreviousTargetBounds.height();
+
+        if (hasChangedSize) {
+            Log.d(TAG, "targetChangeType= changeBounds");
+            return MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_BOUNDS;
+        }
+        Log.d(TAG, "targetChangeType= changePosition");
+        return MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_POSITION;
     }
 
     @VisibleForTesting
@@ -231,45 +308,85 @@
         };
     }
 
+    @VisibleForTesting
+    public int stopReasonToSessionStopSource(@StopReason int stopReason) {
+        return switch (stopReason) {
+            case StopReason.STOP_HOST_APP ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP;
+            case StopReason.STOP_TARGET_REMOVED ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE;
+            case StopReason.STOP_DEVICE_LOCKED->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK;
+            case StopReason.STOP_PRIVACY_CHIP ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP;
+            case StopReason.STOP_QS_TILE ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE;
+            case StopReason.STOP_USER_SWITCH ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH;
+            case StopReason.STOP_FOREGROUND_SERVICE_CHANGE ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE;
+            case StopReason.STOP_NEW_PROJECTION ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION;
+            case StopReason.STOP_NEW_MEDIA_ROUTE ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE;
+            case StopReason.STOP_ERROR ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR;
+            default ->
+                    MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN;
+        };
+    }
+
     /**
      * Logs that the capturing stopped, either normally or because of error.
      *
      * @param hostUid UID of the package that initiates MediaProjection.
      * @param targetUid UID of the package that is captured if selected.
      */
-    public void logStopped(int hostUid, int targetUid) {
+    public void logStopped(int hostUid, int targetUid, int stopReason) {
         boolean wasCaptureInProgress =
-                mPreviousState
+                mPreviousProjectionState
                         == MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
-        Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress);
+        Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress +
+                " stopReason=" + stopReason);
         writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED,
                 hostUid,
                 targetUid,
                 TIME_SINCE_LAST_ACTIVE_UNKNOWN,
-                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+                MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN,
+                stopReasonToSessionStopSource(stopReason));
 
         if (wasCaptureInProgress) {
             mTimestampStore.registerActiveSessionEnded();
         }
     }
 
-    public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
-        writeStateChanged(hostUid, state, sessionCreationSource);
+    public void notifyProjectionStateChange(
+            int hostUid,
+            int state,
+            int sessionCreationSource,
+            int sessionStopSource
+    ) {
+        writeStateChanged(hostUid, state, sessionCreationSource, sessionStopSource);
     }
 
-    private void writeStateChanged(int hostUid, int state, int sessionCreationSource) {
+    private void writeStateChanged(
+            int hostUid,
+            int state,
+            int sessionCreationSource,
+            int sessionStopSource
+    ) {
         mFrameworkStatsLogWrapper.writeStateChanged(
-                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
+                /* code */ MEDIA_PROJECTION_STATE_CHANGED,
                 /* session_id */ 123,
                 /* state */ state,
-                /* previous_state */ FrameworkStatsLog
-                        .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
+                /* previous_state */ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
                 /* host_uid */ hostUid,
                 /* target_uid */ -1,
                 /* time_since_last_active */ 0,
-                /* creation_source */ sessionCreationSource);
+                /* creation_source */ sessionCreationSource,
+                /* stop_source */ sessionStopSource);
     }
 
     private void writeStateChanged(
@@ -278,17 +395,19 @@
             int hostUid,
             int targetUid,
             int timeSinceLastActive,
-            int creationSource) {
+            int creationSource,
+            int stopSource) {
         mFrameworkStatsLogWrapper.writeStateChanged(
-                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
+                /* code */ MEDIA_PROJECTION_STATE_CHANGED,
                 sessionId,
                 state,
-                mPreviousState,
+                mPreviousProjectionState,
                 hostUid,
                 targetUid,
                 timeSinceLastActive,
-                creationSource);
-        mPreviousState = state;
+                creationSource,
+                stopSource);
+        mPreviousProjectionState = state;
     }
 
     private void writeTargetChanged(
@@ -296,13 +415,23 @@
             int targetType,
             int hostUid,
             int targetUid,
-            int targetWindowingMode) {
+            int targetWindowingMode,
+            int width,
+            int height,
+            int centerX,
+            int centerY,
+            int targetChangeType) {
         mFrameworkStatsLogWrapper.writeTargetChanged(
-                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED,
+                /* code */ MEDIA_PROJECTION_TARGET_CHANGED,
                 sessionId,
                 targetType,
                 hostUid,
                 targetUid,
-                targetWindowingMode);
+                targetWindowingMode,
+                width,
+                height,
+                centerX,
+                centerY,
+                targetChangeType);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 0a0882d..4ea4054 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -18,7 +18,6 @@
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
@@ -29,7 +28,6 @@
 import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
 import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
 import static com.android.server.pm.PackageManagerService.INSTANT_APP_RESOLUTION_PHASE_TWO;
-import static com.android.server.pm.PackageManagerService.INTEGRITY_VERIFICATION_COMPLETE;
 import static com.android.server.pm.PackageManagerService.PACKAGE_VERIFIED;
 import static com.android.server.pm.PackageManagerService.POST_INSTALL;
 import static com.android.server.pm.PackageManagerService.PRUNE_UNUSED_STATIC_SHARED_LIBRARIES;
@@ -149,42 +147,6 @@
 
                 break;
             }
-            case CHECK_PENDING_INTEGRITY_VERIFICATION: {
-                final int verificationId = msg.arg1;
-                final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
-
-                if (state != null && !state.isIntegrityVerificationComplete()) {
-                    final VerifyingSession verifyingSession = state.getVerifyingSession();
-                    final Uri originUri = Uri.fromFile(verifyingSession.mOriginInfo.mResolvedFile);
-
-                    String errorMsg = "Integrity verification timed out for " + originUri;
-                    Slog.i(TAG, errorMsg);
-
-                    state.setIntegrityVerificationResult(
-                            getDefaultIntegrityVerificationResponse());
-
-                    if (getDefaultIntegrityVerificationResponse()
-                            == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
-                        Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
-                    } else {
-                        verifyingSession.setReturnCode(
-                                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
-                                errorMsg);
-                    }
-
-                    if (state.areAllVerificationsComplete()) {
-                        mPm.mPendingVerification.remove(verificationId);
-                    }
-
-                    Trace.asyncTraceEnd(
-                            TRACE_TAG_PACKAGE_MANAGER,
-                            "integrity_verification",
-                            verificationId);
-
-                    verifyingSession.handleIntegrityVerificationFinished();
-                }
-                break;
-            }
             case PACKAGE_VERIFIED: {
                 final int verificationId = msg.arg1;
 
@@ -205,42 +167,6 @@
 
                 break;
             }
-            case INTEGRITY_VERIFICATION_COMPLETE: {
-                final int verificationId = msg.arg1;
-
-                final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
-                if (state == null) {
-                    Slog.w(TAG, "Integrity verification with id " + verificationId
-                            + " not found. It may be invalid or overridden by verifier");
-                    break;
-                }
-
-                final int response = (Integer) msg.obj;
-                final VerifyingSession verifyingSession = state.getVerifyingSession();
-                final Uri originUri = Uri.fromFile(verifyingSession.mOriginInfo.mResolvedFile);
-
-                state.setIntegrityVerificationResult(response);
-
-                if (response == PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW) {
-                    Slog.i(TAG, "Integrity check passed for " + originUri);
-                } else {
-                    verifyingSession.setReturnCode(
-                            PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
-                            "Integrity check failed for " + originUri);
-                }
-
-                if (state.areAllVerificationsComplete()) {
-                    mPm.mPendingVerification.remove(verificationId);
-                }
-
-                Trace.asyncTraceEnd(
-                        TRACE_TAG_PACKAGE_MANAGER,
-                        "integrity_verification",
-                        verificationId);
-
-                verifyingSession.handleIntegrityVerificationFinished();
-                break;
-            }
             case INSTANT_APP_RESOLUTION_PHASE_TWO: {
                 InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
                         mPm.snapshotComputer(),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a4f7d0d..cc41b73 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -923,8 +923,8 @@
     static final int ENABLE_ROLLBACK_TIMEOUT = 22;
     static final int DEFERRED_NO_KILL_POST_DELETE = 23;
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
-    static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
-    static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
+    // static final int UNUSED = 25;
+    // static final int UNUSED = 26;
     static final int DOMAIN_VERIFICATION = 27;
     static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
     static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;
@@ -7048,12 +7048,10 @@
             return mSettings.isPermissionUpgradeNeeded(userId);
         }
 
+        @Deprecated
         @Override
         public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
-            final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE);
-            msg.arg1 = verificationId;
-            msg.obj = verificationResult;
-            mHandler.sendMessage(msg);
+          // Do nothing.
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index 0b6ccc4..63c2ee2 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -43,8 +43,6 @@
 
     private boolean mRequiredVerificationPassed;
 
-    private boolean mIntegrityVerificationComplete;
-
     /**
      * Create a new package verification state where {@code requiredVerifierUid} is the user ID for
      * the package that must reply affirmative before things can continue.
@@ -213,15 +211,7 @@
         return mExtendedTimeoutUids.get(uid, false);
     }
 
-    void setIntegrityVerificationResult(int code) {
-        mIntegrityVerificationComplete = true;
-    }
-
-    boolean isIntegrityVerificationComplete() {
-        return mIntegrityVerificationComplete;
-    }
-
     boolean areAllVerificationsComplete() {
-        return mIntegrityVerificationComplete && isVerificationComplete();
+        return isVerificationComplete();
     }
 }
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index f7eb29f..542ae8e 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -28,7 +28,6 @@
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
@@ -87,11 +86,6 @@
      * Whether verification is enabled by default.
      */
     private static final boolean DEFAULT_VERIFY_ENABLE = true;
-
-    /**
-     * Whether integrity verification is enabled by default.
-     */
-    private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true;
     /**
      * The default maximum time to wait for the integrity verification to return in
      * milliseconds.
@@ -129,7 +123,6 @@
     private final boolean mUserActionRequired;
     private final int mUserActionRequiredType;
     private boolean mWaitForVerificationToComplete;
-    private boolean mWaitForIntegrityVerificationToComplete;
     private boolean mWaitForEnableRollbackToComplete;
     private int mRet = PackageManager.INSTALL_SUCCEEDED;
     private String mErrorMessage = null;
@@ -217,7 +210,6 @@
                 new PackageVerificationState(this);
         mPm.mPendingVerification.append(verificationId, verificationState);
 
-        sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
         sendPackageVerificationRequest(
                 verificationId, pkgLite, verificationState);
 
@@ -270,89 +262,6 @@
         mPm.mHandler.sendMessageDelayed(msg, rollbackTimeout);
     }
 
-    /**
-     * Send a request to check the integrity of the package.
-     */
-    void sendIntegrityVerificationRequest(
-            int verificationId,
-            PackageInfoLite pkgLite,
-            PackageVerificationState verificationState) {
-        if (!isIntegrityVerificationEnabled()) {
-            // Consider the integrity check as passed.
-            verificationState.setIntegrityVerificationResult(
-                    PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-            return;
-        }
-
-        final Intent integrityVerification =
-                new Intent(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
-
-        integrityVerification.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
-                PACKAGE_MIME_TYPE);
-
-        final int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                | Intent.FLAG_RECEIVER_FOREGROUND;
-        integrityVerification.addFlags(flags);
-
-        integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId);
-        integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName);
-        integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode);
-        integrityVerification.putExtra(EXTRA_LONG_VERSION_CODE, pkgLite.getLongVersionCode());
-        populateInstallerExtras(integrityVerification);
-
-        // send to integrity component only.
-        integrityVerification.setPackage("android");
-
-        final BroadcastOptions options = BroadcastOptions.makeBasic();
-
-        mPm.mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
-                /* receiverPermission= */ null,
-                /* appOp= */ AppOpsManager.OP_NONE,
-                /* options= */ options.toBundle(),
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        final Message msg =
-                                mPm.mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION);
-                        msg.arg1 = verificationId;
-                        mPm.mHandler.sendMessageDelayed(msg, getIntegrityVerificationTimeout());
-                    }
-                }, /* scheduler= */ null,
-                /* initialCode= */ 0,
-                /* initialData= */ null,
-                /* initialExtras= */ null);
-
-        Trace.asyncTraceBegin(
-                TRACE_TAG_PACKAGE_MANAGER, "integrity_verification", verificationId);
-
-        // stop the copy until verification succeeds.
-        mWaitForIntegrityVerificationToComplete = true;
-    }
-
-
-    /**
-     * Get the integrity verification timeout.
-     *
-     * @return verification timeout in milliseconds
-     */
-    private long getIntegrityVerificationTimeout() {
-        long timeout = Settings.Global.getLong(mPm.mContext.getContentResolver(),
-                Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
-                DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
-        // The setting can be used to increase the timeout but not decrease it, since that is
-        // equivalent to disabling the integrity component.
-        return Math.max(timeout, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
-    }
-
-    /**
-     * Check whether or not integrity verification has been enabled.
-     */
-    private boolean isIntegrityVerificationEnabled() {
-        // We are not exposing this as a user-configurable setting because we don't want to provide
-        // an easy way to get around the integrity check.
-        return DEFAULT_INTEGRITY_VERIFY_ENABLE;
-    }
 
     /**
      * Send a request to verifier(s) to verify the package if necessary.
@@ -827,11 +736,6 @@
         handleReturnCode();
     }
 
-    void handleIntegrityVerificationFinished() {
-        mWaitForIntegrityVerificationToComplete = false;
-        handleReturnCode();
-    }
-
     void handleRollbackEnabled() {
         // TODO(b/112431924): Consider halting the install if we
         // couldn't enable rollback.
@@ -840,7 +744,7 @@
     }
 
     void handleReturnCode() {
-        if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
+        if (mWaitForVerificationToComplete
                 || mWaitForEnableRollbackToComplete) {
             return;
         }
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 e49dc82..976999c 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -426,6 +426,7 @@
     private static final int TRON_COMPILATION_REASON_PREBUILT = 23;
     private static final int TRON_COMPILATION_REASON_VDEX = 24;
     private static final int TRON_COMPILATION_REASON_BOOT_AFTER_MAINLINE_UPDATE = 25;
+    private static final int TRON_COMPILATION_REASON_CLOUD = 26;
 
     // The annotation to add as a suffix to the compilation reason when dexopt was
     // performed with dex metadata.
@@ -460,6 +461,8 @@
                 return TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
             case "install-bulk-secondary-downgraded" :
                 return TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+            case "cloud":
+                return TRON_COMPILATION_REASON_CLOUD;
             // These are special markers for dex metadata installation that do not
             // have an equivalent as a system property.
             case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 9ccf040..77bda9d 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1228,10 +1228,7 @@
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     void setSafeModeAlarm() {
-        final boolean isFlagSafeModeConfigEnabled = mVcnContext.getFeatureFlags().safeModeConfig();
-        logVdbg("isFlagSafeModeConfigEnabled " + isFlagSafeModeConfigEnabled);
-
-        if (isFlagSafeModeConfigEnabled && !mConnectionConfig.isSafeModeEnabled()) {
+        if (!mConnectionConfig.isSafeModeEnabled()) {
             logVdbg("setSafeModeAlarm: safe mode disabled");
             return;
         }
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index b884079..102306f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.SynchronousUserSwitchObserver;
 import android.app.UidObserver;
 import android.content.BroadcastReceiver;
@@ -74,6 +75,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /** Controls all the system settings related to vibration. */
@@ -147,9 +149,6 @@
                     PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT));
 
-    private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
-            new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
-
     /** Listener for changes on vibration settings. */
     interface OnVibratorSettingsChanged {
         /** Callback triggered when any of the vibrator settings change. */
@@ -158,15 +157,18 @@
 
     private final Object mLock = new Object();
     private final Context mContext;
-    private final String mSystemUiPackage;
     @VisibleForTesting
     final SettingsContentObserver mSettingObserver;
     @VisibleForTesting
-    final SettingsBroadcastReceiver mSettingChangeReceiver;
+    final RingerModeBroadcastReceiver mRingerModeBroadcastReceiver;
+    @VisibleForTesting
+    final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
     @VisibleForTesting
     final VibrationUidObserver mUidObserver;
     @VisibleForTesting
     final VibrationUserSwitchObserver mUserSwitchObserver;
+    @VisibleForTesting
+    final VibrationLowPowerModeListener mLowPowerModeListener;
 
     @GuardedBy("mLock")
     private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -180,10 +182,13 @@
     @GuardedBy("mLock")
     @Nullable
     private PowerManagerInternal mPowerManagerInternal;
+    @GuardedBy("mLock")
     @Nullable
     private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
 
     @GuardedBy("mLock")
+    private String mSystemUiPackage;
+    @GuardedBy("mLock")
     private boolean mVibrateInputDevices;
     @GuardedBy("mLock")
     private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray();
@@ -205,11 +210,11 @@
         mContext = context;
         mVibrationConfig = config;
         mSettingObserver = new SettingsContentObserver(handler);
-        mSettingChangeReceiver = new SettingsBroadcastReceiver();
+        mRingerModeBroadcastReceiver = new RingerModeBroadcastReceiver();
+        mBatteryBroadcastReceiver = new BatteryBroadcastReceiver();
         mUidObserver = new VibrationUidObserver();
         mUserSwitchObserver = new VibrationUserSwitchObserver();
-        mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
-                .getSystemUiServiceComponent().getPackageName();
+        mLowPowerModeListener = new VibrationLowPowerModeListener();
 
         VibrationEffect clickEffect = createEffectFromResource(
                 com.android.internal.R.array.config_virtualKeyVibePattern);
@@ -233,18 +238,34 @@
     }
 
     public void onSystemReady() {
-        PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
-        AudioManager am = mContext.getSystemService(AudioManager.class);
-        int ringerMode = (am == null) ? mRingerMode : am.getRingerModeInternal();
+        onSystemReady(LocalServices.getService(PackageManagerInternal.class),
+                LocalServices.getService(PowerManagerInternal.class),
+                ActivityManager.getService(),
+                LocalServices.getService(VirtualDeviceManagerInternal.class),
+                mContext.getSystemService(AudioManager.class));
+    }
+
+    @VisibleForTesting
+    void onSystemReady(PackageManagerInternal packageManagerInternal,
+            PowerManagerInternal powerManagerInternal,
+            IActivityManager activityManagerInternal,
+            @Nullable VirtualDeviceManagerInternal virtualDeviceManagerInternal,
+            @Nullable AudioManager audioManager) {
+        int ringerMode = (audioManager == null)
+                ? AudioManager.RINGER_MODE_NORMAL
+                : audioManager.getRingerModeInternal();
+        String sysUiPackage = packageManagerInternal.getSystemUiServiceComponent().getPackageName();
 
         synchronized (mLock) {
-            mPowerManagerInternal = pm;
-            mAudioManager = am;
+            mPowerManagerInternal = powerManagerInternal;
+            mVirtualDeviceManagerInternal = virtualDeviceManagerInternal;
+            mAudioManager = audioManager;
             mRingerMode = ringerMode;
+            mSystemUiPackage = sysUiPackage;
         }
 
         try {
-            ActivityManager.getService().registerUidObserver(mUidObserver,
+            activityManagerInternal.registerUidObserver(mUidObserver,
                     ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
                     ActivityManager.PROCESS_STATE_UNKNOWN, /* callingPackage= */ null);
         } catch (RemoteException e) {
@@ -252,32 +273,16 @@
         }
 
         try {
-            ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+            activityManagerInternal.registerUserSwitchObserver(mUserSwitchObserver, TAG);
         } catch (RemoteException e) {
             // ignored; both services live in system_server
         }
 
-        pm.registerLowPowerModeObserver(
-                new PowerManagerInternal.LowPowerModeListener() {
-                    @Override
-                    public int getServiceType() {
-                        return PowerManager.ServiceType.VIBRATION;
-                    }
+        powerManagerInternal.registerLowPowerModeObserver(mLowPowerModeListener);
 
-                    @Override
-                    public void onLowPowerModeChanged(PowerSaveState result) {
-                        boolean shouldNotifyListeners;
-                        synchronized (mLock) {
-                            shouldNotifyListeners = result.batterySaverEnabled != mBatterySaverMode;
-                            mBatterySaverMode = result.batterySaverEnabled;
-                        }
-                        if (shouldNotifyListeners) {
-                            notifyListeners();
-                        }
-                    }
-                });
-
-        registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
+        mContext.registerReceiver(mRingerModeBroadcastReceiver,
+                new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
@@ -301,12 +306,7 @@
 
         if (mVibrationConfig.ignoreVibrationsOnWirelessCharger()) {
             Intent batteryStatus = mContext.registerReceiver(
-                    new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            updateBatteryInfo(intent);
-                        }
-                    },
+                    mBatteryBroadcastReceiver,
                     new IntentFilter(Intent.ACTION_BATTERY_CHANGED),
                     Context.RECEIVER_NOT_EXPORTED);
             // After registering the receiver for battery status, process the sticky broadcast that
@@ -476,8 +476,10 @@
     public boolean shouldCancelVibrationOnScreenOff(@NonNull CallerInfo callerInfo,
             long vibrationStartUptimeMillis) {
         PowerManagerInternal pm;
+        String sysUiPackageName;
         synchronized (mLock) {
             pm = mPowerManagerInternal;
+            sysUiPackageName = mSystemUiPackage;
         }
         if (pm != null) {
             // The SleepData from PowerManager may refer to a more recent sleep than the broadcast
@@ -501,7 +503,7 @@
         }
         // Only allow vibrations from System packages to continue vibrating when the screen goes off
         return callerInfo.uid != Process.SYSTEM_UID && callerInfo.uid != 0
-                && !mSystemUiPackage.equals(callerInfo.opPkg);
+                && !Objects.equals(sysUiPackageName, callerInfo.opPkg);
     }
 
     /**
@@ -782,11 +784,6 @@
                 UserHandle.USER_ALL);
     }
 
-    private void registerSettingsChangeReceiver(IntentFilter intentFilter) {
-        mContext.registerReceiver(mSettingChangeReceiver, intentFilter,
-                Context.RECEIVER_EXPORTED_UNAUDITED);
-    }
-
     @Nullable
     private VibrationEffect createEffectFromResource(int resId) {
         return createEffectFromResource(mContext.getResources(), resId);
@@ -833,12 +830,11 @@
     }
 
     private boolean isAppRunningOnAnyVirtualDevice(int uid) {
-        if (mVirtualDeviceManagerInternal == null) {
-            mVirtualDeviceManagerInternal =
-                    LocalServices.getService(VirtualDeviceManagerInternal.class);
+        VirtualDeviceManagerInternal vdm;
+        synchronized (mLock) {
+            vdm = mVirtualDeviceManagerInternal;
         }
-        return mVirtualDeviceManagerInternal != null
-                && mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(uid);
+        return vdm != null && vdm.isAppRunningOnAnyVirtualDevice(uid);
     }
 
     /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@@ -857,7 +853,7 @@
 
     /** Implementation of {@link BroadcastReceiver} to update on ringer mode change. */
     @VisibleForTesting
-    final class SettingsBroadcastReceiver extends BroadcastReceiver {
+    final class RingerModeBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -868,6 +864,18 @@
         }
     }
 
+    /** Implementation of {@link BroadcastReceiver} to update on battery mode change. */
+    @VisibleForTesting
+    final class BatteryBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                updateBatteryInfo(intent);
+            }
+        }
+    }
+
     /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
     @VisibleForTesting
     final class VibrationUidObserver extends UidObserver {
@@ -913,4 +921,25 @@
             update();
         }
     }
+
+    /** Implementation of {@link PowerManagerInternal.LowPowerModeListener} for low battery. */
+    @VisibleForTesting
+    final class VibrationLowPowerModeListener implements PowerManagerInternal.LowPowerModeListener {
+        @Override
+        public int getServiceType() {
+            return PowerManager.ServiceType.VIBRATION;
+        }
+
+        @Override
+        public void onLowPowerModeChanged(PowerSaveState result) {
+            boolean shouldNotifyListeners;
+            synchronized (mLock) {
+                shouldNotifyListeners = result.batterySaverEnabled != mBatterySaverMode;
+                mBatterySaverMode = result.batterySaverEnabled;
+            }
+            if (shouldNotifyListeners) {
+                notifyListeners();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 7e7073c..1e882c4 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -32,6 +32,7 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.media.projection.IMediaProjectionManager;
+import android.media.projection.StopReason;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -226,6 +227,7 @@
                                 + "size %s",
                         mDisplayContent.getDisplayId(), recordedContentBounds,
                         recordedContentOrientation, surfaceSize);
+
                 updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(),
                         recordedContentBounds, surfaceSize);
             } else {
@@ -290,12 +292,12 @@
      * Ensure recording does not fall back to the display stack; ensure the recording is stopped
      * and the client notified by tearing down the virtual display.
      */
-    private void stopMediaProjection() {
+    private void stopMediaProjection(@StopReason int stopReason) {
         ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
                 "Content Recording: Stop MediaProjection on virtual display %d",
                 mDisplayContent.getDisplayId());
         if (mMediaProjectionManager != null) {
-            mMediaProjectionManager.stopActiveProjection();
+            mMediaProjectionManager.stopActiveProjection(stopReason);
         }
     }
 
@@ -502,7 +504,7 @@
         if (shouldExitTaskRecording) {
             // Clean up the cached session first to ensure recording doesn't re-start, since
             // tearing down the display will generate display events which will trickle back here.
-            stopMediaProjection();
+            stopMediaProjection(StopReason.STOP_ERROR);
         }
     }
 
@@ -594,9 +596,13 @@
         mLastRecordedBounds = new Rect(recordedContentBounds);
         mLastConsumingSurfaceSize.x = surfaceSize.x;
         mLastConsumingSurfaceSize.y = surfaceSize.y;
-        // Request to notify the client about the resize.
-        mMediaProjectionManager.notifyActiveProjectionCapturedContentResized(
-                mLastRecordedBounds.width(), mLastRecordedBounds.height());
+
+        // Request to notify the client about the updated bounds.
+        mMediaProjectionManager.notifyCaptureBoundsChanged(
+                mContentRecordingSession.getContentToRecord(),
+                mContentRecordingSession.getTargetUid(),
+                mLastRecordedBounds
+        );
     }
 
     /**
@@ -636,7 +642,7 @@
         clearContentRecordingSession();
         // Clean up the cached session first to ensure recording doesn't re-start, since
         // tearing down the display will generate display events which will trickle back here.
-        stopMediaProjection();
+        stopMediaProjection(StopReason.STOP_TARGET_REMOVED);
     }
 
     // WindowContainerListener
@@ -669,10 +675,10 @@
     }
 
     @VisibleForTesting interface MediaProjectionManagerWrapper {
-        void stopActiveProjection();
-        void notifyActiveProjectionCapturedContentResized(int width, int height);
+        void stopActiveProjection(@StopReason int stopReason);
         void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible);
         void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
+        void notifyCaptureBoundsChanged(int contentToRecord, int targetUid, Rect captureBounds);
     }
 
     private static final class RemoteMediaProjectionManagerWrapper implements
@@ -686,7 +692,7 @@
         }
 
         @Override
-        public void stopActiveProjection() {
+        public void stopActiveProjection(@StopReason int stopReason) {
             fetchMediaProjectionManager();
             if (mIMediaProjectionManager == null) {
                 return;
@@ -695,7 +701,7 @@
                 ProtoLog.e(WM_DEBUG_CONTENT_RECORDING,
                         "Content Recording: stopping active projection for display %d",
                         mDisplayId);
-                mIMediaProjectionManager.stopActiveProjection();
+                mIMediaProjectionManager.stopActiveProjection(stopReason);
             } catch (RemoteException e) {
                 ProtoLog.e(WM_DEBUG_CONTENT_RECORDING,
                         "Content Recording: Unable to tell MediaProjectionManagerService to stop "
@@ -705,23 +711,6 @@
         }
 
         @Override
-        public void notifyActiveProjectionCapturedContentResized(int width, int height) {
-            fetchMediaProjectionManager();
-            if (mIMediaProjectionManager == null) {
-                return;
-            }
-            try {
-                mIMediaProjectionManager.notifyActiveProjectionCapturedContentResized(width,
-                        height);
-            } catch (RemoteException e) {
-                ProtoLog.e(WM_DEBUG_CONTENT_RECORDING,
-                        "Content Recording: Unable to tell MediaProjectionManagerService about "
-                                + "resizing the active projection: %s",
-                        e);
-            }
-        }
-
-        @Override
         public void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible) {
             fetchMediaProjectionManager();
             if (mIMediaProjectionManager == null) {
@@ -754,6 +743,22 @@
             }
         }
 
+        @Override
+        public void notifyCaptureBoundsChanged(int contentToRecord, int targetUid,
+                Rect captureBounds) {
+            fetchMediaProjectionManager();
+            if (mIMediaProjectionManager == null) {
+                return;
+            }
+            try {
+                mIMediaProjectionManager.notifyCaptureBoundsChanged(
+                        contentToRecord, targetUid, captureBounds);
+            } catch (RemoteException e) {
+                ProtoLog.e(WM_DEBUG_CONTENT_RECORDING,
+                        "Content Recording: Unable to tell log bounds change: %s", e);
+            }
+        }
+
         private void fetchMediaProjectionManager() {
             if (mIMediaProjectionManager != null) {
                 return;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index d042b38..dd5f613 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1385,7 +1385,16 @@
         // Commit wallpaper visibility after activity, because usually the wallpaper target token is
         // an activity, and wallpaper's visibility depends on activity's visibility.
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
-            final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
+            final WindowContainer<?> wc = mParticipants.valueAt(i);
+            WallpaperWindowToken wt = wc.asWallpaperToken();
+            if (!Flags.ensureWallpaperInTransitions()) {
+                if (wt == null) {
+                    final WindowState windowState = wc.asWindowState();
+                    if (windowState != null) {
+                        wt = windowState.mToken.asWallpaperToken();
+                    }
+                }
+            }
             if (wt == null) continue;
             final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget();
             final boolean isTargetInvisible = target == null || !target.mToken.isVisible();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9ecaca1..78d9056 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2742,7 +2742,7 @@
      * Expands the given rectangle by the region of window resize handle for freeform window.
      * @param inOutRect The rectangle to update.
      */
-    private void adjustRegionInFreefromWindowMode(Rect inOutRect) {
+    private void adjustRegionInFreeformWindowMode(Rect inOutRect) {
         if (!inFreeformWindowingMode()) {
             return;
         }
@@ -2790,7 +2790,7 @@
                 }
             }
         }
-        adjustRegionInFreefromWindowMode(mTmpRect);
+        adjustRegionInFreeformWindowMode(mTmpRect);
         outRegion.set(mTmpRect);
         cropRegionToRootTaskBoundsIfNeeded(outRegion);
     }
@@ -3584,7 +3584,7 @@
         }
 
         rootTask.getDimBounds(mTmpRect);
-        adjustRegionInFreefromWindowMode(mTmpRect);
+        adjustRegionInFreeformWindowMode(mTmpRect);
         region.op(mTmpRect, Region.Op.INTERSECT);
     }
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java
index a93e8ad..97f1bd4 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -574,57 +574,16 @@
         assertTrue(state.isInstallAllowed());
     }
 
-    public void testAreAllVerificationsComplete_onlyVerificationPasses() {
+    public void testAreAllVerificationsComplete() {
         PackageVerificationState state = new PackageVerificationState(null);
         state.addRequiredVerifierUid(REQUIRED_UID_1);
         assertFalse(state.areAllVerificationsComplete());
 
         state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
 
-        assertFalse(state.areAllVerificationsComplete());
-    }
-
-    public void testAreAllVerificationsComplete_onlyIntegrityCheckPasses() {
-        PackageVerificationState state = new PackageVerificationState(null);
-        state.addRequiredVerifierUid(REQUIRED_UID_1);
-        assertFalse(state.areAllVerificationsComplete());
-
-        state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-
-        assertFalse(state.areAllVerificationsComplete());
-    }
-
-    public void testAreAllVerificationsComplete_bothPasses() {
-        PackageVerificationState state = new PackageVerificationState(null);
-        state.addRequiredVerifierUid(REQUIRED_UID_1);
-        assertFalse(state.areAllVerificationsComplete());
-
-        state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
-
         assertTrue(state.areAllVerificationsComplete());
     }
 
-    public void testAreAllVerificationsComplete_onlyVerificationFails() {
-        PackageVerificationState state = new PackageVerificationState(null);
-        state.addRequiredVerifierUid(REQUIRED_UID_1);
-        assertFalse(state.areAllVerificationsComplete());
-
-        state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
-
-        assertFalse(state.areAllVerificationsComplete());
-    }
-
-    public void testAreAllVerificationsComplete_onlyIntegrityCheckFails() {
-        PackageVerificationState state = new PackageVerificationState(null);
-        state.addRequiredVerifierUid(REQUIRED_UID_1);
-        assertFalse(state.areAllVerificationsComplete());
-
-        state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
-
-        assertFalse(state.areAllVerificationsComplete());
-    }
-
     private void processOnTimeout(PackageVerificationState state, int code, int uid) {
         // CHECK_PENDING_VERIFICATION handler.
         assertFalse("Verification should not be marked as complete yet",
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
deleted file mode 100644
index fd22118..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2019 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.integrity;
-
-import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
-import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
-import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
-import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Settings;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.R;
-import com.android.server.compat.PlatformCompat;
-import com.android.server.testutils.TestUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
-@RunWith(JUnit4.class)
-public class AppIntegrityManagerServiceImplTest {
-    private static final String TEST_APP_PATH =
-            "AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk";
-
-    private static final String TEST_APP_TWO_CERT_PATH =
-            "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
-
-    private static final String TEST_APP_SOURCE_STAMP_PATH =
-            "AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk";
-
-    private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
-    private static final String VERSION = "version";
-    private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
-
-    private static final String PACKAGE_NAME = "com.test.app";
-
-    private static final long VERSION_CODE = 100;
-    private static final String INSTALLER = "com.long.random.test.installer.name";
-
-    // These are obtained by running the test and checking logcat.
-    private static final String APP_CERT =
-            "F14CFECF5070874C05D3D2FA98E046BE20BDE02A0DC74BAF6B59C6A0E4C06850";
-    // We use SHA256 for package names longer than 32 characters.
-    private static final String INSTALLER_SHA256 =
-            "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
-    private static final String SOURCE_STAMP_CERTIFICATE_HASH =
-            "C6E737809CEF2B08CC6694892215F82A5E8FBC3C2A0F6212770310B90622D2D9";
-
-    private static final String DUMMY_APP_TWO_CERTS_CERT_1 =
-            "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94";
-    private static final String DUMMY_APP_TWO_CERTS_CERT_2 =
-            "94366E0A80F3A3F0D8171A15760B88E228CD6E1101F0414C98878724FBE70147";
-
-    private static final String PLAY_STORE_PKG = "com.android.vending";
-    private static final String ADB_INSTALLER = "adb";
-    private static final String PLAY_STORE_CERT = "play_store_cert";
-
-    @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    @Mock PackageManagerInternal mPackageManagerInternal;
-    @Mock PlatformCompat mPlatformCompat;
-    @Mock Context mMockContext;
-    @Mock Resources mMockResources;
-    @Mock Handler mHandler;
-
-    private final Context mRealContext = InstrumentationRegistry.getTargetContext();
-
-    private PackageManager mSpyPackageManager;
-    private File mTestApk;
-    private File mTestApkTwoCerts;
-    private File mTestApkSourceStamp;
-
-    // under test
-    private AppIntegrityManagerServiceImpl mService;
-
-    @Before
-    public void setup() throws Exception {
-        mTestApk = File.createTempFile("AppIntegrity", ".apk");
-        try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_PATH)) {
-            Files.copy(inputStream, mTestApk.toPath(), REPLACE_EXISTING);
-        }
-
-        mTestApkTwoCerts = File.createTempFile("AppIntegrityTwoCerts", ".apk");
-        try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
-            Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
-        }
-
-        mTestApkSourceStamp = File.createTempFile("AppIntegritySourceStamp", ".apk");
-        try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_SOURCE_STAMP_PATH)) {
-            Files.copy(inputStream, mTestApkSourceStamp.toPath(), REPLACE_EXISTING);
-        }
-
-        mService =
-                new AppIntegrityManagerServiceImpl(
-                        mMockContext,
-                        mPackageManagerInternal,
-                        mHandler);
-
-        mSpyPackageManager = spy(mRealContext.getPackageManager());
-        // setup mocks to prevent NPE
-        when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
-        when(mMockContext.getResources()).thenReturn(mMockResources);
-        when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
-        // These are needed to override the Settings.Global.get result.
-        when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
-        setIntegrityCheckIncludesRuleProvider(true);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mTestApk.delete();
-        mTestApkTwoCerts.delete();
-        mTestApkSourceStamp.delete();
-    }
-
-    @Test
-    public void broadcastReceiverRegistration() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        ArgumentCaptor<IntentFilter> intentFilterCaptor =
-                ArgumentCaptor.forClass(IntentFilter.class);
-
-        verify(mMockContext).registerReceiver(any(), intentFilterCaptor.capture(), any(), any());
-        assertEquals(1, intentFilterCaptor.getValue().countActions());
-        assertEquals(
-                Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION,
-                intentFilterCaptor.getValue().getAction(0));
-        assertEquals(1, intentFilterCaptor.getValue().countDataTypes());
-        assertEquals(PACKAGE_MIME_TYPE, intentFilterCaptor.getValue().getDataType(0));
-    }
-
-    @Test
-    public void handleBroadcast_allow() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        Intent intent = makeVerificationIntent();
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        verify(mPackageManagerInternal)
-                .setIntegrityVerificationResult(
-                        1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-    }
-
-    private void allowlistUsAsRuleProvider() {
-        Resources mockResources = mock(Resources.class);
-        when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
-                .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
-        when(mMockContext.getResources()).thenReturn(mockResources);
-    }
-
-    private void runJobInHandler() {
-        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-        // sendMessageAtTime is the first non-final method in the call chain when "post" is invoked.
-        verify(mHandler).sendMessageAtTime(messageCaptor.capture(), anyLong());
-        messageCaptor.getValue().getCallback().run();
-    }
-
-    private void makeUsSystemApp() throws Exception {
-        makeUsSystemApp(true);
-    }
-
-    private void makeUsSystemApp(boolean isSystemApp) throws Exception {
-        PackageInfo packageInfo =
-                mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, 0);
-        if (isSystemApp) {
-            packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
-        } else {
-            packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
-        }
-        doReturn(packageInfo)
-                .when(mSpyPackageManager)
-                .getPackageInfo(eq(TEST_FRAMEWORK_PACKAGE), anyInt());
-        when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
-    }
-
-    private Intent makeVerificationIntent() throws Exception {
-        PackageInfo packageInfo =
-                mRealContext
-                        .getPackageManager()
-                        .getPackageInfo(
-                                TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNING_CERTIFICATES);
-        doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
-        doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
-        doReturn(new String[]{INSTALLER}).when(mSpyPackageManager).getPackagesForUid(anyInt());
-        return makeVerificationIntent(INSTALLER);
-    }
-
-    private Intent makeVerificationIntent(String installer) throws Exception {
-        Intent intent = new Intent();
-        intent.setDataAndType(Uri.fromFile(mTestApk), PACKAGE_MIME_TYPE);
-        intent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
-        intent.putExtra(EXTRA_VERIFICATION_ID, 1);
-        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, PACKAGE_NAME);
-        intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, installer);
-        intent.putExtra(
-                EXTRA_VERIFICATION_INSTALLER_UID,
-                mMockContext.getPackageManager().getPackageUid(installer, /* flags= */ 0));
-        intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, VERSION_CODE);
-        return intent;
-    }
-
-    private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception {
-        int value = shouldInclude ? 1 : 0;
-        Settings.Global.putInt(
-                mRealContext.getContentResolver(),
-                Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
-                value);
-        assertThat(
-                        Settings.Global.getInt(
-                                        mRealContext.getContentResolver(),
-                                        Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
-                                        -1)
-                                == 1)
-                .isEqualTo(shouldInclude);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 425bb15..c6e2427c 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -66,10 +66,12 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Rect;
 import android.media.projection.IMediaProjection;
 import android.media.projection.IMediaProjectionCallback;
 import android.media.projection.IMediaProjectionWatcherCallback;
 import android.media.projection.ReviewGrantedConsentResult;
+import android.media.projection.StopReason;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Looper;
@@ -492,7 +494,8 @@
                 .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
         service.onKeyguardLockedStateChanged(true);
 
-        verify(mMediaProjectionMetricsLogger).logStopped(UID, TARGET_UID_UNKNOWN);
+        verify(mMediaProjectionMetricsLogger)
+            .logStopped(UID, TARGET_UID_UNKNOWN, StopReason.STOP_DEVICE_LOCKED);
         assertThat(service.getActiveProjectionInfo()).isNull();
         assertThat(mIMediaProjectionCallback.mLatch.await(5, TimeUnit.SECONDS)).isTrue();
     }
@@ -525,7 +528,7 @@
         MediaProjectionManagerService.MediaProjection projection =
                 startProjectionPreconditions(service);
 
-        projection.stop();
+        projection.stop(StopReason.STOP_UNKNOWN);
 
         verifyZeroInteractions(mMediaProjectionMetricsLogger);
     }
@@ -538,10 +541,10 @@
                 startProjectionPreconditions(service);
         projection.start(mIMediaProjectionCallback);
 
-        projection.stop();
+        final @StopReason int stopReason = StopReason.STOP_UNKNOWN;
+        projection.stop(stopReason);
 
-        verify(mMediaProjectionMetricsLogger)
-                .logStopped(UID, TARGET_UID_UNKNOWN);
+        verify(mMediaProjectionMetricsLogger).logStopped(UID, TARGET_UID_UNKNOWN, stopReason);
     }
 
     @Test
@@ -556,15 +559,16 @@
                 .setContentRecordingSession(any(ContentRecordingSession.class));
         service.setContentRecordingSession(DISPLAY_SESSION);
 
-        projection.stop();
+        final @StopReason int stopReason = StopReason.STOP_UNKNOWN;
+        projection.stop(stopReason);
 
-        verify(mMediaProjectionMetricsLogger)
-                .logStopped(UID, TARGET_UID_FULL_SCREEN);
+        verify(mMediaProjectionMetricsLogger).logStopped(UID, TARGET_UID_FULL_SCREEN, stopReason);
     }
 
     @Test
     public void stop_taskSession_logsHostUidAndTargetUid() throws Exception {
         int targetUid = 1234;
+        int stopReason = StopReason.STOP_UNKNOWN;
         MediaProjectionManagerService service =
                 new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
         MediaProjectionManagerService.MediaProjection projection =
@@ -577,9 +581,9 @@
         taskSession.setTargetUid(targetUid);
         service.setContentRecordingSession(taskSession);
 
-        projection.stop();
+        projection.stop(stopReason);
 
-        verify(mMediaProjectionMetricsLogger).logStopped(UID, targetUid);
+        verify(mMediaProjectionMetricsLogger).logStopped(UID, targetUid, stopReason);
     }
 
     @Test
@@ -614,7 +618,7 @@
         projection.start(mIMediaProjectionCallback);
         assertThat(projection.isValid()).isTrue();
 
-        projection.stop();
+        projection.stop(StopReason.STOP_UNKNOWN);
 
         // Second start - so not valid.
         projection.start(mIMediaProjectionCallback);
@@ -668,7 +672,7 @@
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(
                 service);
         projection.start(mIMediaProjectionCallback);
-        projection.stop();
+        projection.stop(StopReason.STOP_UNKNOWN);
         // Second start - so not valid.
         projection.start(mIMediaProjectionCallback);
 
@@ -684,7 +688,7 @@
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(
                 service);
         projection.start(mIMediaProjectionCallback);
-        projection.stop();
+        projection.stop(StopReason.STOP_UNKNOWN);
 
         // Second start - so not valid.
         projection.start(mIMediaProjectionCallback);
@@ -923,6 +927,26 @@
                 projection.uid, targetUid, WINDOWING_MODE_MULTI_WINDOW);
     }
 
+    @Test
+    public void notifyCaptureBoundsChanged_forwardsToLoggerAndResizeCallbacks() throws Exception {
+        int targetUid = 123;
+        mService =
+                new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+        ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class));
+        taskSession.setTargetUid(targetUid);
+        mService.setContentRecordingSession(taskSession);
+
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        projection.start(mIMediaProjectionCallback);
+
+        Rect newBounds = new Rect(0, 0, 1000, 2000);
+        mService.notifyCaptureBoundsChanged(RECORD_CONTENT_TASK, targetUid, newBounds);
+
+        verify(mMediaProjectionMetricsLogger)
+                .logChangedCaptureBounds(RECORD_CONTENT_TASK, projection.uid, targetUid, newBounds);
+    }
+
     /**
      * Executes and validates scenario where the consent result indicates the projection ends.
      */
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index 72ce9fe..c727bb6 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -32,6 +32,17 @@
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
@@ -46,6 +57,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.media.projection.StopReason;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -81,6 +93,7 @@
 
     private static final int TEST_WINDOWING_MODE = 987;
     private static final int TEST_CONTENT_TO_RECORD = 654;
+    private static final int TEST_STOP_SOURCE = 321;
 
     @Mock private FrameworkStatsLogWrapper mFrameworkStatsLogWrapper;
     @Mock private MediaProjectionSessionIdGenerator mSessionIdGenerator;
@@ -136,6 +149,14 @@
     }
 
     @Test
+    public void logInitiated_logsUnknownStopSource() {
+        mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+
+        verifyStopSourceLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
+    @Test
     public void logInitiated_noPreviousSession_logsUnknownTimeSinceLastActive() {
         when(mTimestampStore.timeSinceLastActiveSession()).thenReturn(null);
 
@@ -177,7 +198,7 @@
 
     @Test
     public void logStopped_logsStateChangedAtomId() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifyStateChangedAtomIdLogged();
     }
@@ -187,42 +208,49 @@
         int currentSessionId = 987;
         when(mSessionIdGenerator.getCurrentSessionId()).thenReturn(currentSessionId);
 
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifySessionIdLogged(currentSessionId);
     }
 
     @Test
     public void logStopped_logsStateStopped() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
     }
 
     @Test
     public void logStopped_logsHostUid() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
     public void logStopped_logsTargetUid() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifyStageChangedTargetUidLogged(TEST_TARGET_UID);
     }
 
     @Test
+    public void logStopped_logsStopSource() {
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, StopReason.STOP_UNKNOWN);
+
+        verifyStopSourceLogged(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
+    @Test
     public void logStopped_logsUnknownTimeSinceLastActive() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifyTimeSinceLastActiveSessionLogged(-1);
     }
 
     @Test
     public void logStopped_logsUnknownSessionCreationSource() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verifyCreationSourceLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
@@ -230,7 +258,7 @@
 
     @Test
     public void logStopped_logsPreviousState() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN);
 
@@ -238,7 +266,7 @@
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
 
-        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE);
+        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE);
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
     }
@@ -247,14 +275,14 @@
     public void logStopped_capturingWasInProgress_registersActiveSessionEnded() {
         mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID);
 
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verify(mTimestampStore).registerActiveSessionEnded();
     }
 
     @Test
     public void logStopped_capturingWasNotInProgress_doesNotRegistersActiveSessionEnded() {
-        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
+        mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE);
 
         verify(mTimestampStore, never()).registerActiveSessionEnded();
     }
@@ -314,6 +342,14 @@
     }
 
     @Test
+    public void logInProgress_logsUnknownSessionStopSource() {
+        mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID);
+
+        verifyStopSourceLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
+    @Test
     public void logInProgress_logsPreviousState() {
         mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
         verifyPreviousStateLogged(
@@ -323,7 +359,7 @@
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
 
-        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE);
+        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE);
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS);
 
@@ -387,6 +423,14 @@
     }
 
     @Test
+    public void logPermissionRequestDisplayed_logsUnknownSessionStopSource() {
+        mLogger.logPermissionRequestDisplayed(TEST_HOST_UID);
+
+        verifyStopSourceLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
+    @Test
     public void logPermissionRequestDisplayed_logsPreviousState() {
         mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
         verifyPreviousStateLogged(
@@ -396,7 +440,7 @@
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
 
-        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE);
+        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE);
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED);
 
@@ -460,6 +504,14 @@
     }
 
     @Test
+    public void logAppSelectorDisplayed_logsUnknownSessionStopSource() {
+        mLogger.logAppSelectorDisplayed(TEST_HOST_UID);
+
+        verifyStopSourceLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
+    @Test
     public void logAppSelectorDisplayed_logsPreviousState() {
         mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
         verifyPreviousStateLogged(
@@ -469,7 +521,7 @@
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
 
-        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE);
+        mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE);
         verifyPreviousStateLogged(
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED);
 
@@ -536,6 +588,14 @@
     }
 
     @Test
+    public void logProjectionPermissionRequestCancelled_logsUnknownStopSource() {
+        mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+        verifyStopSourceLogged(
+                MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
+    @Test
     public void logWindowingModeChanged_logsTargetChangedAtomId() {
         mLogger.logChangedWindowingMode(
                 TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
@@ -614,6 +674,42 @@
                 .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN);
     }
 
+    @Test
+    public void testStopReasonToSessionStopSource() {
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_HOST_APP))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_TARGET_REMOVED))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_DEVICE_LOCKED))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_PRIVACY_CHIP))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_QS_TILE))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_USER_SWITCH))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_FOREGROUND_SERVICE_CHANGE))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_NEW_PROJECTION))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_NEW_MEDIA_ROUTE))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_ERROR))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR);
+
+        mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_UNKNOWN))
+                .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN);
+    }
+
     private void verifyStateChangedAtomIdLogged() {
         verify(mFrameworkStatsLogWrapper)
                 .writeStateChanged(
@@ -624,7 +720,8 @@
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ anyInt(),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifyStateLogged(int state) {
@@ -637,7 +734,8 @@
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ anyInt(),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifyStateChangedHostUidLogged(int hostUid) {
@@ -650,7 +748,8 @@
                         eq(hostUid),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ anyInt(),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifyCreationSourceLogged(int creationSource) {
@@ -663,7 +762,22 @@
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ anyInt(),
-                        eq(creationSource));
+                        eq(creationSource),
+                        /* stopSource= */ anyInt());
+    }
+
+    private void verifyStopSourceLogged(int stopSource) {
+        verify(mFrameworkStatsLogWrapper)
+                .writeStateChanged(
+                        /* code= */ anyInt(),
+                        /* sessionId= */ anyInt(),
+                        /* state= */ anyInt(),
+                        /* previousState= */ anyInt(),
+                        /* hostUid= */ anyInt(),
+                        /* targetUid= */ anyInt(),
+                        /* timeSinceLastActive= */ anyInt(),
+                        /* stopSource= */ anyInt(),
+                        eq(stopSource));
     }
 
     private void verifyStageChangedTargetUidLogged(int targetUid) {
@@ -676,7 +790,8 @@
                         /* hostUid= */ anyInt(),
                         eq(targetUid),
                         /* timeSinceLastActive= */ anyInt(),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifyTimeSinceLastActiveSessionLogged(int timeSinceLastActiveSession) {
@@ -689,7 +804,8 @@
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ eq(timeSinceLastActiveSession),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifySessionIdLogged(int newSessionId) {
@@ -702,7 +818,8 @@
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ anyInt(),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifyPreviousStateLogged(int previousState) {
@@ -715,7 +832,8 @@
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
                         /* timeSinceLastActive= */ anyInt(),
-                        /* creationSource= */ anyInt());
+                        /* creationSource= */ anyInt(),
+                        /* stopSource= */ anyInt());
     }
 
     private void verifyTargetChangedAtomIdLogged() {
@@ -726,7 +844,12 @@
                         /* targetType= */ anyInt(),
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
-                        /* targetWindowingMode= */ anyInt());
+                        /* targetWindowingMode= */ anyInt(),
+                        /* width= */ anyInt(),
+                        /* height= */ anyInt(),
+                        /* centerX= */ anyInt(),
+                        /* centerY= */ anyInt(),
+                        /* targetChangeType= */ anyInt());
     }
 
     private void verifyTargetTypeLogged(int targetType) {
@@ -737,7 +860,12 @@
                         eq(targetType),
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
-                        /* targetWindowingMode= */ anyInt());
+                        /* targetWindowingMode= */ anyInt(),
+                        /* width= */ anyInt(),
+                        /* height= */ anyInt(),
+                        /* centerX= */ anyInt(),
+                        /* centerY= */ anyInt(),
+                        /* targetChangeType= */ anyInt());
     }
 
     private void verifyTargetChangedHostUidLogged(int hostUid) {
@@ -748,7 +876,12 @@
                         /* targetType= */ anyInt(),
                         eq(hostUid),
                         /* targetUid= */ anyInt(),
-                        /* targetWindowingMode= */ anyInt());
+                        /* targetWindowingMode= */ anyInt(),
+                        /* width= */ anyInt(),
+                        /* height= */ anyInt(),
+                        /* centerX= */ anyInt(),
+                        /* centerY= */ anyInt(),
+                        /* targetChangeType= */ anyInt());
     }
 
     private void verifyTargetChangedTargetUidLogged(int targetUid) {
@@ -759,7 +892,12 @@
                         /* targetType= */ anyInt(),
                         /* hostUid= */ anyInt(),
                         eq(targetUid),
-                        /* targetWindowingMode= */ anyInt());
+                        /* targetWindowingMode= */ anyInt(),
+                        /* width= */ anyInt(),
+                        /* height= */ anyInt(),
+                        /* centerX= */ anyInt(),
+                        /* centerY= */ anyInt(),
+                        /* targetChangeType= */ anyInt());
     }
 
     private void verifyWindowingModeLogged(int targetWindowingMode) {
@@ -770,6 +908,11 @@
                         /* targetType= */ anyInt(),
                         /* hostUid= */ anyInt(),
                         /* targetUid= */ anyInt(),
-                        eq(targetWindowingMode));
+                        eq(targetWindowingMode),
+                        /* width= */ anyInt(),
+                        /* height= */ anyInt(),
+                        /* centerX= */ anyInt(),
+                        /* centerY= */ anyInt(),
+                        /* targetChangeType= */ anyInt());
     }
 }
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 492f108..e4a6efd 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -44,7 +44,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -52,12 +53,14 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.media.AudioManager;
 import android.os.Handler;
@@ -79,12 +82,10 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
-import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.vibrator.VibrationSession.CallerInfo;
 import com.android.server.vibrator.VibrationSession.Status;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -101,8 +102,7 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
-    private static final int OLD_USER_ID = 123;
-    private static final int NEW_USER_ID = 456;
+    private static final int USER_ID = 123;
     private static final int UID = 1;
     private static final int VIRTUAL_DEVICE_ID = 1;
     private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -132,13 +132,12 @@
     @Mock private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
     @Mock private AudioManager mAudioManagerMock;
+    @Mock private IActivityManager mActivityManagerMock;
     @Mock private VibrationConfig mVibrationConfigMock;
 
     private TestLooper mTestLooper;
     private ContextWrapper mContextSpy;
     private VibrationSettings mVibrationSettings;
-    private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
-    private BroadcastReceiver mRegisteredBatteryBroadcastReceiver;
 
     @Before
     public void setUp() throws Exception {
@@ -146,24 +145,20 @@
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
-        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-        when(mContextSpy.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManagerMock);
-        doAnswer(invocation -> {
-            mRegisteredPowerModeListener = invocation.getArgument(0);
-            return null;
-        }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
+        doReturn(contentResolver).when(mContextSpy).getContentResolver();
+
+        // Make sure broadcast receivers are not registered for this test, to avoid flakes.
+        doReturn(null).when(mContextSpy)
+                .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class), anyInt());
         when(mPackageManagerInternalMock.getSystemUiServiceComponent())
                 .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
 
-        removeServicesForTest();
-        addServicesForTest();
-
         setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
 
         setIgnoreVibrationsOnWirelessCharger(false);
-        createSystemReadyVibrationSettings();
 
         mockGoToSleep(/* goToSleepTime= */ 0, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+        createSystemReadyVibrationSettings();
     }
 
     private void createSystemReadyVibrationSettings() {
@@ -177,38 +172,18 @@
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        mVibrationSettings.onSystemReady();
-    }
-
-    private void removeServicesForTest() {
-        LocalServices.removeServiceForTest(PowerManagerInternal.class);
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
-    }
-
-    private void addServicesForTest() {
-        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
-        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
-        LocalServices.addService(VirtualDeviceManagerInternal.class,
-                mVirtualDeviceManagerInternalMock);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        removeServicesForTest();
+        mVibrationSettings.onSystemReady(mPackageManagerInternalMock, mPowerManagerInternalMock,
+                mActivityManagerMock, mVirtualDeviceManagerInternalMock, mAudioManagerMock);
     }
 
     @Test
     public void create_withOnlyRequiredSystemServices() {
-        // The only core services that we depend on are PowerManager and PackageManager
-        removeServicesForTest();
-        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
-        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
-        when(mContextSpy.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(null);
-
         VibrationSettings minimalVibrationSettings = new VibrationSettings(mContextSpy,
                 new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
-        minimalVibrationSettings.onSystemReady();
+
+        // The only core services that we depend on are Power, Package and Activity managers
+        minimalVibrationSettings.onSystemReady(mPackageManagerInternalMock,
+                mPowerManagerInternalMock, mActivityManagerMock, null, null);
     }
 
     @Test
@@ -216,8 +191,8 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitching(USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(USER_ID);
 
         verify(mListenerMock, times(2)).onChange();
     }
@@ -227,9 +202,9 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
         verify(mListenerMock, times(2)).onChange();
@@ -251,9 +226,9 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-        mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
-        mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); // No change.
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); // Noop.
 
         verify(mListenerMock, times(2)).onChange();
     }
@@ -268,10 +243,9 @@
         mVibrationSettings.removeListener(mListenerMock);
 
         // Trigger multiple observers manually.
-        mVibrationSettings.mSettingObserver.onChange(false);
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(USER_ID);
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
         verifyNoMoreInteractions(mListenerMock);
@@ -312,11 +286,12 @@
 
     @Test
     public void wirelessChargingVibrationsEnabled_doesNotRegisterBatteryReceiver_allowsAnyUsage() {
-        setBatteryReceiverRegistrationResult(getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS));
         setIgnoreVibrationsOnWirelessCharger(false);
         createSystemReadyVibrationSettings();
 
-        assertNull(mRegisteredBatteryBroadcastReceiver);
+        verify(mContextSpy, never()).registerReceiver(any(BroadcastReceiver.class),
+                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
+
         for (int usage : ALL_USAGES) {
             assertVibrationNotIgnoredForUsage(usage);
         }
@@ -324,7 +299,6 @@
 
     @Test
     public void shouldIgnoreVibration_noBatteryIntentWhenSystemReady_allowsAnyUsage() {
-        setBatteryReceiverRegistrationResult(null);
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
@@ -336,7 +310,9 @@
     @Test
     public void shouldIgnoreVibration_onNonWirelessChargerWhenSystemReady_allowsAnyUsage() {
         Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
-        setBatteryReceiverRegistrationResult(nonWirelessChargingIntent);
+        doReturn(nonWirelessChargingIntent).when(mContextSpy).registerReceiver(
+                any(BroadcastReceiver.class),
+                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
@@ -348,7 +324,9 @@
     @Test
     public void shouldIgnoreVibration_onWirelessChargerWhenSystemReady_doesNotAllowFromAnyUsage() {
         Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
-        setBatteryReceiverRegistrationResult(wirelessChargingIntent);
+        doReturn(wirelessChargingIntent).when(mContextSpy).registerReceiver(
+                any(BroadcastReceiver.class),
+                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
@@ -359,13 +337,12 @@
 
     @Test
     public void shouldIgnoreVibration_receivesWirelessChargingIntent_doesNotAllowFromAnyUsage() {
-        Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
-        setBatteryReceiverRegistrationResult(nonWirelessChargingIntent);
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
         Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
-        mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, wirelessChargingIntent);
+        mVibrationSettings.mBatteryBroadcastReceiver.onReceive(
+                mContextSpy, wirelessChargingIntent);
 
         for (int usage : ALL_USAGES) {
             assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
@@ -374,17 +351,21 @@
 
     @Test
     public void shouldIgnoreVibration_receivesNonWirelessChargingIntent_allowsAnyUsage() {
-        Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
-        setBatteryReceiverRegistrationResult(wirelessChargingIntent);
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
+
+        Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
+        mVibrationSettings.mBatteryBroadcastReceiver.onReceive(
+                mContextSpy, wirelessChargingIntent);
+
         // Check that initially, all usages are ignored due to the wireless charging.
         for (int usage : ALL_USAGES) {
             assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
         }
 
         Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
-        mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, nonWirelessChargingIntent);
+        mVibrationSettings.mBatteryBroadcastReceiver.onReceive(
+                mContextSpy, nonWirelessChargingIntent);
 
         for (int usage : ALL_USAGES) {
             assertVibrationNotIgnoredForUsage(usage);
@@ -401,7 +382,7 @@
                 USAGE_HARDWARE_FEEDBACK
         ));
 
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
 
         for (int usage : ALL_USAGES) {
             if (expectedAllowedVibrations.contains(usage)) {
@@ -414,7 +395,7 @@
 
     @Test
     public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() {
-        mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
 
         for (int usage : ALL_USAGES) {
             assertVibrationNotIgnoredForUsage(usage);
@@ -606,7 +587,7 @@
 
         // Testing the broadcast flow manually.
         when(mAudioManagerMock.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
         assertVibrationIgnoredForUsage(USAGE_RINGTONE, Status.IGNORED_FOR_RINGER_MODE);
@@ -862,16 +843,15 @@
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
 
         // Test early update of settings based on new user id.
-        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
-                NEW_USER_ID);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW, USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitching(USER_ID);
         assertEquals(VIBRATION_INTENSITY_LOW,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
 
         // Test later update of settings for UserHandle.USER_CURRENT.
         putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
                 UserHandle.USER_CURRENT);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(USER_ID);
         assertEquals(VIBRATION_INTENSITY_LOW,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
     }
@@ -1019,7 +999,7 @@
     private void setRingerMode(int ringerMode) {
         when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);
         // Mock AudioManager broadcast of internal ringer mode change.
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
     }
 
@@ -1034,14 +1014,6 @@
         return new CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
     }
 
-    private void setBatteryReceiverRegistrationResult(Intent result) {
-        doAnswer(invocation -> {
-            mRegisteredBatteryBroadcastReceiver = invocation.getArgument(0);
-            return result;
-        }).when(mContextSpy).registerReceiver(any(BroadcastReceiver.class),
-                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
-    }
-
     private Intent getBatteryChangedIntent(int extraPluggedValue) {
         Intent batteryIntent = new Intent(Intent.ACTION_BATTERY_CHANGED);
         batteryIntent.putExtra(EXTRA_PLUGGED, extraPluggedValue);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index e443696..a5303b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -49,6 +49,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.media.projection.StopReason;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.ContentRecordingSession;
@@ -191,7 +192,7 @@
         mContentRecorder.setContentRecordingSession(session);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
-        verify(mMediaProjectionManagerWrapper).stopActiveProjection();
+        verify(mMediaProjectionManagerWrapper).stopActiveProjection(StopReason.STOP_ERROR);
     }
 
     @Test
@@ -203,7 +204,7 @@
         mContentRecorder.setContentRecordingSession(invalidTaskSession);
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
-        verify(mMediaProjectionManagerWrapper).stopActiveProjection();
+        verify(mMediaProjectionManagerWrapper).stopActiveProjection(StopReason.STOP_ERROR);
     }
 
     @Test
@@ -288,8 +289,7 @@
                 mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
 
         // No resize is issued, only the initial transformations when we started recording.
-        verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(),
-                anyFloat());
+        verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat());
         verify(mTransaction).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
                 anyFloat(), anyFloat());
     }
@@ -364,19 +364,18 @@
 
         // WHEN a configuration change arrives, and the recorded content is a different size.
         Configuration configuration = mTask.getConfiguration();
-        configuration.windowConfiguration.setBounds(new Rect(0, 0, recordedWidth, recordedHeight));
-        configuration.windowConfiguration.setAppBounds(
-                new Rect(0, 0, recordedWidth, recordedHeight));
+        Rect newBounds = new Rect(0, 0, recordedWidth, recordedHeight);
+        configuration.windowConfiguration.setBounds(newBounds);
+        configuration.windowConfiguration.setAppBounds(newBounds);
         mTask.onConfigurationChanged(configuration);
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
 
         // THEN content in the captured DisplayArea is scaled to fit the surface size.
-        verify(mTransaction, atLeastOnce()).setMatrix(eq(mRecordedSurface), anyFloat(), eq(0f),
-                eq(0f),
-                anyFloat());
+        verify(mTransaction, atLeastOnce()).setMatrix(
+                eq(mRecordedSurface), anyFloat(), eq(0f), eq(0f), anyFloat());
         // THEN the resize callback is notified.
-        verify(mMediaProjectionManagerWrapper).notifyActiveProjectionCapturedContentResized(
-                recordedWidth, recordedHeight);
+        verify(mMediaProjectionManagerWrapper).notifyCaptureBoundsChanged(
+                mTaskSession.getContentToRecord(), mTaskSession.getTargetUid(), newBounds);
     }
 
     @Test
@@ -627,7 +626,7 @@
 
         mTask.removeImmediately();
 
-        verify(mMediaProjectionManagerWrapper).stopActiveProjection();
+        verify(mMediaProjectionManagerWrapper).stopActiveProjection(StopReason.STOP_TARGET_REMOVED);
     }
 
     @Test
@@ -662,8 +661,8 @@
         int xInset = (mSurfaceSize.x - scaledWidth) / 2;
         verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0);
         // THEN the resize callback is notified.
-        verify(mMediaProjectionManagerWrapper).notifyActiveProjectionCapturedContentResized(
-                displayAreaBounds.width(), displayAreaBounds.height());
+        verify(mMediaProjectionManagerWrapper).notifyCaptureBoundsChanged(
+                mDisplaySession.getContentToRecord(), mDisplaySession.getTargetUid(),  displayAreaBounds);
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 76be232..74db6a5 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -659,7 +659,6 @@
 
     private void verifySetSafeModeAlarm(
             boolean safeModeEnabledByCaller,
-            boolean safeModeConfigFlagEnabled,
             boolean expectingSafeModeEnabled)
             throws Exception {
         final VcnGatewayConnectionConfig config =
@@ -670,7 +669,6 @@
                 mock(VcnGatewayConnection.Dependencies.class);
         setUpWakeupMessage(
                 mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM, deps);
-        doReturn(safeModeConfigFlagEnabled).when(mFeatureFlags).safeModeConfig();
 
         final VcnGatewayConnection connection =
                 new VcnGatewayConnection(
@@ -694,37 +692,19 @@
     }
 
     @Test
-    public void testSafeModeEnabled_configFlagEnabled() throws Exception {
+    public void testSafeModeEnabled() throws Exception {
         verifySetSafeModeAlarm(
                 true /* safeModeEnabledByCaller */,
-                true /* safeModeConfigFlagEnabled */,
                 true /* expectingSafeModeEnabled */);
     }
 
     @Test
-    public void testSafeModeEnabled_configFlagDisabled() throws Exception {
-        verifySetSafeModeAlarm(
-                true /* safeModeEnabledByCaller */,
-                false /* safeModeConfigFlagEnabled */,
-                true /* expectingSafeModeEnabled */);
-    }
-
-    @Test
-    public void testSafeModeDisabled_configFlagEnabled() throws Exception {
+    public void testSafeModeDisabled() throws Exception {
         verifySetSafeModeAlarm(
                 false /* safeModeEnabledByCaller */,
-                true /* safeModeConfigFlagEnabled */,
                 false /* expectingSafeModeEnabled */);
     }
 
-    @Test
-    public void testSafeModeDisabled_configFlagDisabled() throws Exception {
-        verifySetSafeModeAlarm(
-                false /* safeModeEnabledByCaller */,
-                false /* safeModeConfigFlagEnabled */,
-                true /* expectingSafeModeEnabled */);
-    }
-
     private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
         triggerChildOpened();
         mTestLooper.dispatchAll();