Merge "[sb] status_bar_simple_fragment -> status_bar_root_modernization" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index eb49739..664b3dd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -314,6 +314,7 @@
     field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
     field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
     field public static final String TURN_SCREEN_ON = "android.permission.TURN_SCREEN_ON";
+    field @FlaggedApi("android.app.enable_tv_implicit_enter_pip_restriction") public static final String TV_IMPLICIT_ENTER_PIP = "android.permission.TV_IMPLICIT_ENTER_PIP";
     field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
     field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
     field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION";
@@ -8839,7 +8840,7 @@
     field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
     field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
     field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
-    field public static final String PROPERTY_RETURN_VALUE = "returnValue";
+    field public static final String PROPERTY_RETURN_VALUE = "android_app_appfunctions_returnvalue";
     field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
     field public static final int RESULT_CANCELLED = 2001; // 0x7d1
     field public static final int RESULT_DENIED = 1000; // 0x3e8
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a9b181d..30fe244 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -706,6 +706,7 @@
     field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_READ_MEDIA_VISUAL_USER_SELECTED = "android:read_media_visual_user_selected";
+    field @FlaggedApi("android.permission.flags.platform_oxygen_saturation_enabled") public static final String OPSTR_READ_OXYGEN_SATURATION = "android:read_oxygen_saturation";
     field @FlaggedApi("android.permission.flags.platform_skin_temperature_enabled") public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
     field public static final String OPSTR_READ_WRITE_HEALTH_DATA = "android:read_write_health_data";
     field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio";
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7a1c759..3fccc17 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -30,6 +30,7 @@
 
 import static java.lang.Character.MIN_VALUE;
 
+import android.Manifest;
 import android.annotation.AnimRes;
 import android.annotation.CallSuper;
 import android.annotation.CallbackExecutor;
@@ -3193,6 +3194,16 @@
         return ActivityTaskManager.getMaxNumPictureInPictureActions(this);
     }
 
+    private boolean isImplicitEnterPipProhibited() {
+        PackageManager pm = getPackageManager();
+        if (android.app.Flags.enableTvImplicitEnterPipRestriction()) {
+            return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                    && pm.checkPermission(Manifest.permission.TV_IMPLICIT_ENTER_PIP,
+                    getPackageName()) == PackageManager.PERMISSION_DENIED;
+        }
+        return false;
+    }
+
     /**
      * @return Whether this device supports picture-in-picture.
      */
@@ -9192,6 +9203,8 @@
         }
         dispatchActivityPreResumed();
 
+        mCanEnterPictureInPicture = true;
+
         mFragments.execPendingActions();
 
         mLastNonConfigurationInstances = null;
@@ -9243,6 +9256,11 @@
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:"
                     + mComponent.getClassName());
         }
+
+        if (isImplicitEnterPipProhibited()) {
+            mCanEnterPictureInPicture = false;
+        }
+
         dispatchActivityPrePaused();
         mDoReportFullyDrawn = false;
         mFragments.dispatchPause();
@@ -9265,6 +9283,10 @@
 
     final void performUserLeaving() {
         onUserInteraction();
+
+        if (isImplicitEnterPipProhibited()) {
+            mCanEnterPictureInPicture = false;
+        }
         onUserLeaveHint();
     }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 38c8583..fd70f4f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1621,9 +1621,12 @@
      */
     public static final int OP_RANGING = AppOpEnums.APP_OP_RANGING;
 
+    /** @hide Access to read oxygen saturation. */
+    public static final int OP_READ_OXYGEN_SATURATION = AppOpEnums.APP_OP_READ_OXYGEN_SATURATION;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 152;
+    public static final int _NUM_OP = 153;
 
     /**
      * All app ops represented as strings.
@@ -1779,6 +1782,7 @@
             OPSTR_READ_HEART_RATE,
             OPSTR_READ_SKIN_TEMPERATURE,
             OPSTR_RANGING,
+            OPSTR_READ_OXYGEN_SATURATION,
     })
     public @interface AppOpString {}
 
@@ -2521,6 +2525,11 @@
     @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
     public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate";
 
+    /** @hide Access to read oxygen saturation. */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_PLATFORM_OXYGEN_SATURATION_ENABLED)
+    public static final String OPSTR_READ_OXYGEN_SATURATION = "android:read_oxygen_saturation";
+
     /** @hide Access to read skin temperature. */
     @SystemApi
     @FlaggedApi(Flags.FLAG_PLATFORM_SKIN_TEMPERATURE_ENABLED)
@@ -2608,6 +2617,7 @@
             // Health
             Flags.replaceBodySensorPermissionEnabled() ? OP_READ_HEART_RATE : OP_NONE,
             Flags.platformSkinTemperatureEnabled() ? OP_READ_SKIN_TEMPERATURE : OP_NONE,
+            Flags.platformOxygenSaturationEnabled() ? OP_READ_OXYGEN_SATURATION : OP_NONE,
     };
 
     /**
@@ -3129,6 +3139,11 @@
             .setPermission(Flags.rangingPermissionEnabled()?
                 Manifest.permission.RANGING : null)
             .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_OXYGEN_SATURATION, OPSTR_READ_OXYGEN_SATURATION,
+            "READ_OXYGEN_SATURATION").setPermission(
+                Flags.platformOxygenSaturationEnabled()
+                    ? HealthPermissions.READ_OXYGEN_SATURATION : null)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index 41bb622..1557815 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -111,8 +111,8 @@
      * Returns the function parameters. The key is the parameter name, and the value is the
      * parameter value.
      *
-     * <p>The bundle may have missing parameters. Developers are advised to implement defensive
-     * handling measures.
+     * <p>The {@link GenericDocument} may have missing parameters. Developers are advised to
+     * implement defensive handling measures.
      *
      * @see AppFunctionManager on how to determine the expected parameters.
      */
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index cdf02e6..ced4b55 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -71,7 +71,7 @@
      *
      * <p>See {@link #getResultDocument} for more information on extracting the return value.
      */
-    public static final String PROPERTY_RETURN_VALUE = "returnValue";
+    public static final String PROPERTY_RETURN_VALUE = "android_app_appfunctions_returnvalue";
 
     /**
      * The call was successful.
diff --git a/core/java/android/app/multitasking.aconfig b/core/java/android/app/multitasking.aconfig
index 9a64519..c8455c1 100644
--- a/core/java/android/app/multitasking.aconfig
+++ b/core/java/android/app/multitasking.aconfig
@@ -8,3 +8,11 @@
     description: "Enables PiP UI state callback on entering"
     bug: "303718131"
 }
+
+flag {
+    name: "enable_tv_implicit_enter_pip_restriction"
+    is_exported: true
+    namespace: "tv_system_ui"
+    description: "Enables restrictions to PiP entry on TV for setAutoEnterEnabled and lifecycle methods"
+    bug: "283115999"
+}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index ff0bb25..cc57dc0 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -398,6 +398,7 @@
          * Retrieve the raw Intent contained in this Item.
          */
         public Intent getIntent() {
+            Intent.maybeMarkAsMissingCreatorToken(mIntent);
             return mIntent;
         }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6fa5a9b..c054b79 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -87,6 +87,7 @@
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.expresslog.Counter;
 
@@ -108,6 +109,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
+import java.util.function.Consumer;
 
 /**
  * An intent is an abstract description of an operation to be performed.  It
@@ -892,6 +894,20 @@
     public static void maybeMarkAsMissingCreatorToken(Object object) {
         if (object instanceof Intent intent) {
             maybeMarkAsMissingCreatorTokenInternal(intent);
+        } else if (object instanceof Parcelable[] parcelables) {
+            for (Parcelable p : parcelables) {
+                if (p instanceof Intent intent) {
+                    maybeMarkAsMissingCreatorTokenInternal(intent);
+                }
+            }
+        } else if (object instanceof ArrayList parcelables) {
+            int N = parcelables.size();
+            for (int i = 0; i < N; i++) {
+                Object p = parcelables.get(i);
+                if (p instanceof Intent intent) {
+                    maybeMarkAsMissingCreatorTokenInternal(intent);
+                }
+            }
         }
     }
 
@@ -12204,7 +12220,70 @@
         // Stores a creator token for an intent embedded as an extra intent in a top level intent,
         private IBinder mCreatorToken;
         // Stores all extra keys whose values are intents for a top level intent.
-        private ArraySet<String> mExtraIntentKeys;
+        private ArraySet<NestedIntentKey> mNestedIntentKeys;
+    }
+
+    /**
+     * @hide
+     */
+    public static class NestedIntentKey {
+        /** @hide */
+        @IntDef(flag = true, prefix = {"NESTED_INTENT_KEY_TYPE"}, value = {
+                NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL,
+                NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY,
+                NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST,
+                NESTED_INTENT_KEY_TYPE_CLIP_DATA,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        private @interface NestedIntentKeyType {
+        }
+
+        /**
+         * This flag indicates the key is for an extra parcel in mExtras.
+         */
+        private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL = 1 << 0;
+
+        /**
+         * This flag indicates the key is for an extra parcel array in mExtras and the index is the
+         * index of that array.
+         */
+        private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY = 1 << 1;
+
+        /**
+         * This flag indicates the key is for an extra parcel list in mExtras and the index is the
+         * index of that list.
+         */
+        private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST = 1 << 2;
+
+        /**
+         * This flag indicates the key is for an extra parcel in mClipData.mItems.
+         */
+        private static final int NESTED_INTENT_KEY_TYPE_CLIP_DATA = 1 << 3;
+
+        // type can be a short or even byte. But then probably cannot use @IntDef?? Also not sure
+        // if it is necessary.
+        private final @NestedIntentKeyType int mType;
+        private final String mKey;
+        private final int mIndex;
+
+        private NestedIntentKey(@NestedIntentKeyType int type, String key, int index) {
+            this.mType = type;
+            this.mKey = key;
+            this.mIndex = index;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            NestedIntentKey that = (NestedIntentKey) o;
+            return mType == that.mType && mIndex == that.mIndex && Objects.equals(mKey, that.mKey);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mType, mKey, mIndex);
+        }
     }
 
     private @Nullable CreatorTokenInfo mCreatorTokenInfo;
@@ -12227,8 +12306,9 @@
     }
 
     /** @hide */
-    public Set<String> getExtraIntentKeys() {
-        return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys;
+    @VisibleForTesting
+    public Set<NestedIntentKey> getExtraIntentKeys() {
+        return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mNestedIntentKeys;
     }
 
     /** @hide */
@@ -12246,45 +12326,168 @@
      * @hide
      */
     public void collectExtraIntentKeys() {
-        if (!preventIntentRedirect()) return;
+        if (preventIntentRedirect()) {
+            collectNestedIntentKeysRecur(new ArraySet<>());
+        }
+    }
 
-        if (mExtras != null && !mExtras.isEmpty()) {
+    private void collectNestedIntentKeysRecur(Set<Intent> visited) {
+        if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
             for (String key : mExtras.keySet()) {
-                if (mExtras.get(key) instanceof Intent) {
-                    if (mCreatorTokenInfo == null) {
-                        mCreatorTokenInfo = new CreatorTokenInfo();
-                    }
-                    if (mCreatorTokenInfo.mExtraIntentKeys == null) {
-                        mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>();
-                    }
-                    mCreatorTokenInfo.mExtraIntentKeys.add(key);
+                Object value = mExtras.get(key);
+
+                if (value instanceof Intent intent && !visited.contains(intent)) {
+                    handleNestedIntent(intent, visited, new NestedIntentKey(
+                            NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0));
+                } else if (value instanceof Parcelable[] parcelables) {
+                    handleParcelableArray(parcelables, key, visited);
+                } else if (value instanceof ArrayList<?> parcelables) {
+                    handleParcelableList(parcelables, key, visited);
+                }
+            }
+        }
+
+        if (mClipData != null) {
+            for (int i = 0; i < mClipData.getItemCount(); i++) {
+                Intent intent = mClipData.getItemAt(i).mIntent;
+                if (intent != null && !visited.contains(intent)) {
+                    handleNestedIntent(intent, visited, new NestedIntentKey(
+                            NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA, null, i));
                 }
             }
         }
     }
 
+    private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key) {
+        visited.add(intent);
+        if (mCreatorTokenInfo == null) {
+            mCreatorTokenInfo = new CreatorTokenInfo();
+        }
+        if (mCreatorTokenInfo.mNestedIntentKeys == null) {
+            mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>();
+        }
+        mCreatorTokenInfo.mNestedIntentKeys.add(key);
+        intent.collectNestedIntentKeysRecur(visited);
+    }
+
+    private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited) {
+        for (int i = 0; i < parcelables.length; i++) {
+            if (parcelables[i] instanceof Intent intent && !visited.contains(intent)) {
+                handleNestedIntent(intent, visited, new NestedIntentKey(
+                        NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, key, i));
+            }
+        }
+    }
+
+    private void handleParcelableList(ArrayList<?> parcelables, String key, Set<Intent> visited) {
+        for (int i = 0; i < parcelables.size(); i++) {
+            if (parcelables.get(i) instanceof Intent intent && !visited.contains(intent)) {
+                handleNestedIntent(intent, visited, new NestedIntentKey(
+                        NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, key, i));
+            }
+        }
+    }
+
+    private static final Consumer<Intent> CHECK_CREATOR_TOKEN_ACTION = intent -> {
+        intent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
+        if (intent.mExtras != null) {
+            intent.mExtras.enableTokenVerification();
+        }
+    };
+
     /** @hide */
     public void checkCreatorToken() {
-        if (mExtras == null) return;
-        if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) {
-            for (String key : mCreatorTokenInfo.mExtraIntentKeys) {
-                try {
-                    Intent extraIntent = mExtras.getParcelable(key, Intent.class);
-                    if (extraIntent == null) {
-                        Log.w(TAG, "The key {" + key
-                                + "} does not correspond to an intent in the bundle.");
-                        continue;
-                    }
-                    extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
-                } catch (Exception e) {
-                    Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e);
+        forEachNestedCreatorToken(CHECK_CREATOR_TOKEN_ACTION);
+
+        if (mExtras != null) {
+            // mark the bundle as intent extras after calls to getParcelable.
+            // otherwise, the logic to mark missing token would run before
+            // mark trusted creator token present.
+            mExtras.enableTokenVerification();
+        }
+    }
+
+    /** @hide */
+    public void forEachNestedCreatorToken(Consumer<? super Intent> action) {
+        if (mExtras == null && mClipData == null) return;
+
+        if (mCreatorTokenInfo != null && mCreatorTokenInfo.mNestedIntentKeys != null) {
+            int N = mCreatorTokenInfo.mNestedIntentKeys.size();
+            for (int i = 0; i < N; i++) {
+                NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
+                Intent extraIntent = extractIntentFromKey(key);
+
+                if (extraIntent != null) {
+                    action.accept(extraIntent);
+                    extraIntent.forEachNestedCreatorToken(action);
+                } else {
+                    Log.w(TAG, getLogMessageForKey(key));
                 }
             }
         }
-        // mark the bundle as intent extras after calls to getParcelable.
-        // otherwise, the logic to mark missing token would run before
-        // mark trusted creator token present.
-        mExtras.setIsIntentExtra();
+    }
+
+    private Intent extractIntentFromKey(NestedIntentKey key) {
+        switch (key.mType) {
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL:
+                return mExtras == null ? null : mExtras.getParcelable(key.mKey, Intent.class);
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY:
+                if (mExtras == null) return null;
+                Intent[] extraIntents = mExtras.getParcelableArray(key.mKey, Intent.class);
+                if (extraIntents != null && key.mIndex < extraIntents.length) {
+                    return extraIntents[key.mIndex];
+                }
+                break;
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST:
+                if (mExtras == null) return null;
+                ArrayList<Intent> extraIntentsList = mExtras.getParcelableArrayList(key.mKey,
+                        Intent.class);
+                if (extraIntentsList != null && key.mIndex < extraIntentsList.size()) {
+                    return extraIntentsList.get(key.mIndex);
+                }
+                break;
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA:
+                if (mClipData == null) return null;
+                if (key.mIndex < mClipData.getItemCount()) {
+                    ClipData.Item item = mClipData.getItemAt(key.mIndex);
+                    if (item != null) {
+                        return item.mIntent;
+                    }
+                }
+                break;
+        }
+        return null;
+    }
+
+    private String getLogMessageForKey(NestedIntentKey key) {
+        switch (key.mType) {
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL:
+                return "The key {" + key + "} does not correspond to an intent in the bundle.";
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY:
+                if (mExtras.getParcelableArray(key.mKey, Intent.class) == null) {
+                    return "The key {" + key
+                            + "} does not correspond to a Parcelable[] in the bundle.";
+                } else {
+                    return "Parcelable[" + key.mIndex + "] for key {" + key + "} is not an intent.";
+                }
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST:
+                if (mExtras.getParcelableArrayList(key.mKey, Intent.class) == null) {
+                    return "The key {" + key
+                            + "} does not correspond to an ArrayList<Parcelable> in the bundle.";
+                } else {
+                    return "List.get(" + key.mIndex + ") for key {" + key + "} is not an intent.";
+                }
+            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA:
+                if (key.mIndex >= mClipData.getItemCount()) {
+                    return "Index out of range for clipData items. index: " + key.mIndex
+                            + ". item counts: " + mClipData.getItemCount();
+                } else {
+                    return "clipData items at index [" + key.mIndex
+                            + "] is null or does not contain an intent.";
+                }
+            default:
+                return "Unknown key type: " + key.mType;
+        }
     }
 
     /**
@@ -12357,7 +12560,19 @@
             } else {
                 out.writeInt(1);
                 out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken);
-                out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys);
+
+                if (mCreatorTokenInfo.mNestedIntentKeys != null) {
+                    final int N = mCreatorTokenInfo.mNestedIntentKeys.size();
+                    out.writeInt(N);
+                    for (int i = 0; i < N; i++) {
+                        NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
+                        out.writeInt(key.mType);
+                        out.writeString8(key.mKey);
+                        out.writeInt(key.mIndex);
+                    }
+                } else {
+                    out.writeInt(0);
+                }
             }
         }
     }
@@ -12422,7 +12637,18 @@
             if (in.readInt() != 0) {
                 mCreatorTokenInfo = new CreatorTokenInfo();
                 mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
-                mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null);
+
+                N = in.readInt();
+                if (N > 0) {
+                    mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(N);
+                    for (int i = 0; i < N; i++) {
+                        int type = in.readInt();
+                        String key = in.readString8();
+                        int index = in.readInt();
+                        mCreatorTokenInfo.mNestedIntentKeys.append(
+                                new NestedIntentKey(type, key, index));
+                    }
+                }
             }
         }
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index e22c263..1cc0856 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -437,7 +437,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public synchronized void writeToParcel(Parcel dest, int flags) {
         nativeWriteToParcel(dest, mMetadataPtr);
     }
 
@@ -479,7 +479,7 @@
         return getBase(key);
     }
 
-    public void readFromParcel(Parcel in) {
+    public synchronized void readFromParcel(Parcel in) {
         nativeReadFromParcel(in, mMetadataPtr);
         updateNativeAllocation();
     }
@@ -592,28 +592,33 @@
     }
 
     private <T> T getBase(Key<T> key) {
-        int tag;
-        if (key.hasTag()) {
-            tag = key.getTag();
-        } else {
-            tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName());
-            key.cacheTag(tag);
-        }
-        byte[] values = readValues(tag);
-        if (values == null) {
-            // If the key returns null, use the fallback key if exists.
-            // This is to support old key names for the newly published keys.
-            if (key.mFallbackName == null) {
-                return null;
+        int tag, nativeType;
+        byte[] values = null;
+        synchronized (this) {
+            if (key.hasTag()) {
+                tag = key.getTag();
+            } else {
+                tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName());
+                key.cacheTag(tag);
             }
-            tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName);
             values = readValues(tag);
             if (values == null) {
-                return null;
+                // If the key returns null, use the fallback key if exists.
+                // This is to support old key names for the newly published keys.
+                if (key.mFallbackName == null) {
+                    return null;
+                }
+                tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName);
+                values = readValues(tag);
+                if (values == null) {
+                    return null;
+                }
             }
-        }
 
-        int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
+            nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
+        }
+        // This block of code doesn't need to be synchronized since we aren't writing or reading
+        // from the metadata buffer for this instance of CameraMetadataNative.
         Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
         return marshaler.unmarshal(buffer);
@@ -1945,8 +1950,12 @@
         setBase(key.getNativeKey(), value);
     }
 
-    private <T> void setBase(Key<T> key, T value) {
-        int tag;
+    // The whole method needs to be synchronized since we're making
+    // multiple calls to the native layer. From one call to the other (within setBase)
+    // we expect the metadata's properties such as vendor id etc to
+    // stay the same and as a result the whole method should be synchronized for safety.
+    private synchronized <T> void setBase(Key<T> key, T value) {
+        int tag, nativeType;
         if (key.hasTag()) {
             tag = key.getTag();
         } else {
@@ -1959,7 +1968,7 @@
             return;
         } // else update the entry to a new value
 
-        int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
+        nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag);
         Marshaler<T> marshaler = getMarshalerForKey(key, nativeType);
         int size = marshaler.calculateMarshalSize(value);
 
@@ -2162,7 +2171,7 @@
         return true;
     }
 
-    private void updateNativeAllocation() {
+    private synchronized void updateNativeAllocation() {
         long currentBufferSize = nativeGetBufferSize(mMetadataPtr);
 
         if (currentBufferSize != mBufferSize) {
@@ -2245,6 +2254,11 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
 
+    // FastNative doesn't work with synchronized methods and we can do synchronization
+    // wherever needed in the java layer (caller). At some places in java such as
+    // setBase() / getBase(), we do need to synchronize the whole method, so leaving
+    // synchronized out for these native methods.
+
     @FastNative
     private static native long nativeAllocate();
     @FastNative
@@ -2254,28 +2268,41 @@
 
     @FastNative
     private static native void nativeUpdate(long dst, long src);
-    private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
-    private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
-    private static synchronized native void nativeSwap(long ptr, long otherPtr)
+    @FastNative
+    private static native void nativeWriteToParcel(Parcel dest, long ptr);
+    @FastNative
+    private static native void nativeReadFromParcel(Parcel source, long ptr);
+    @FastNative
+    private static native void nativeSwap(long ptr, long otherPtr)
             throws NullPointerException;
     @FastNative
     private static native void nativeSetVendorId(long ptr, long vendorId);
-    private static synchronized native void nativeClose(long ptr);
-    private static synchronized native boolean nativeIsEmpty(long ptr);
-    private static synchronized native int nativeGetEntryCount(long ptr);
-    private static synchronized native long nativeGetBufferSize(long ptr);
+    @FastNative
+    private static native void nativeClose(long ptr);
+    @FastNative
+    private static native boolean nativeIsEmpty(long ptr);
+    @FastNative
+    private static native int nativeGetEntryCount(long ptr);
+    @FastNative
+    private static native long nativeGetBufferSize(long ptr);
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private static synchronized native byte[] nativeReadValues(int tag, long ptr);
-    private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr);
-    private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD
+    @FastNative
+    private static native byte[] nativeReadValues(int tag, long ptr);
+    @FastNative
+    private static native void nativeWriteValues(int tag, byte[] src, long ptr);
+    @FastNative
+    private static native void nativeDump(long ptr) throws IOException; // dump to LOGD
 
-    private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
+    @FastNative
+    private static native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass);
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName)
+    @FastNative
+    private static native int nativeGetTagFromKeyLocal(long ptr, String keyName)
             throws IllegalArgumentException;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag)
+    @FastNative
+    private static native int nativeGetTypeFromTagLocal(long ptr, int tag)
             throws IllegalArgumentException;
     @FastNative
     private static native int nativeGetTagFromKey(String keyName, long vendorId)
@@ -2293,7 +2320,7 @@
      * @throws NullPointerException if other was null
      * @hide
      */
-    public void swap(CameraMetadataNative other) {
+    public synchronized void swap(CameraMetadataNative other) {
         nativeSwap(mMetadataPtr, other.mMetadataPtr);
         mCameraId = other.mCameraId;
         mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
@@ -2308,14 +2335,14 @@
      *
      * @hide
      */
-    public void setVendorId(long vendorId) {
+    public synchronized void setVendorId(long vendorId) {
         nativeSetVendorId(mMetadataPtr, vendorId);
     }
 
     /**
      * @hide
      */
-    public int getEntryCount() {
+    public synchronized int getEntryCount() {
         return nativeGetEntryCount(mMetadataPtr);
     }
 
@@ -2324,7 +2351,7 @@
      *
      * @hide
      */
-    public boolean isEmpty() {
+    public synchronized boolean isEmpty() {
         return nativeIsEmpty(mMetadataPtr);
     }
 
@@ -2343,7 +2370,7 @@
      *
      * @hide
      */
-    public <K>  ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
+    public synchronized <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) {
         if (keyClass == null) {
             throw new NullPointerException();
         }
@@ -2398,7 +2425,7 @@
      *
      * @hide
      */
-    public void writeValues(int tag, byte[] src) {
+    public synchronized void writeValues(int tag, byte[] src) {
         nativeWriteValues(tag, src, mMetadataPtr);
     }
 
@@ -2413,7 +2440,7 @@
      * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
      * @hide
      */
-    public byte[] readValues(int tag) {
+    public synchronized byte[] readValues(int tag) {
         // TODO: Optimization. Native code returns a ByteBuffer instead.
         return nativeReadValues(tag, mMetadataPtr);
     }
@@ -2426,7 +2453,7 @@
      *
      * @hide
      */
-    public void dumpToLog() {
+    public synchronized void dumpToLog() {
         try {
             nativeDump(mMetadataPtr);
         } catch (IOException e) {
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index c18fb0c..99e7d166 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -281,7 +281,7 @@
     }
 
     /** {@hide} */
-    public void setIsIntentExtra() {
+    public void enableTokenVerification() {
         mFlags |= FLAG_VERIFY_TOKENS_PRESENT;
     }
 
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index 72f2de8..dfc11dc 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -67,3 +67,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "cleanup_dream_settings_on_uninstall"
+    namespace: "systemui"
+    description: "Cleans up dream settings if dream package is uninstalled."
+    bug: "338210427"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 618843c..fa06831 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -23882,12 +23882,12 @@
                     } else {
                         draw(canvas);
                     }
-                }
 
-                // For VRR to vote the preferred frame rate
-                if (sToolkitSetFrameRateReadOnlyFlagValue
-                        && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
-                    votePreferredFrameRate();
+                    // For VRR to vote the preferred frame rate
+                    if (sToolkitSetFrameRateReadOnlyFlagValue
+                            && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
+                        votePreferredFrameRate();
+                    }
                 }
             } finally {
                 renderNode.endRecording();
diff --git a/core/res/Android.bp b/core/res/Android.bp
index c66d2b5..73776f0 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -158,6 +158,7 @@
     flags_packages: [
         "android.app.appfunctions.flags-aconfig",
         "android.app.contextualsearch.flags-aconfig",
+        "android.app.flags-aconfig",
         "android.appwidget.flags-aconfig",
         "android.content.pm.flags-aconfig",
         "android.provider.flags-aconfig",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 44bda1c..0e4eb94 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4463,6 +4463,18 @@
                 android:description="@string/permdesc_hideOverlayWindows"
                 android:protectionLevel="normal" />
 
+    <!-- Allows an app to enter Picture-in-Picture mode when the user is not explicitly requesting
+        it. This includes using {@link PictureInPictureParams.Builder#setAutoEnterEnabled} as well
+        as lifecycle methods such as {@link Activity#onUserLeaveHint} and {@link Activity#onPause}
+        to enter PiP when the user leaves the app.
+        This permission should only be used for certain PiP
+        <a href="{@docRoot}training/tv/get-started/multitasking#usage-types">usage types</a>.
+        @FlaggedApi(android.app.Flags.FLAG_ENABLE_TV_IMPLICIT_ENTER_PIP_RESTRICTION)
+     -->
+    <permission android:name="android.permission.TV_IMPLICIT_ENTER_PIP"
+        android:protectionLevel="normal"
+        android:featureFlag="android.app.enable_tv_implicit_enter_pip_restriction" />
+
     <!-- ================================== -->
     <!-- Permissions affecting the system wallpaper -->
     <!-- ================================== -->
diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java
index d169ce3..7bc4abd 100644
--- a/core/tests/coretests/src/android/content/IntentTest.java
+++ b/core/tests/coretests/src/android/content/IntentTest.java
@@ -37,6 +37,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 /**
  *  Build/Install/Run:
  *   atest FrameworksCoreTests:IntentTest
@@ -57,7 +61,12 @@
     public void testReadFromParcelWithExtraIntentKeys() {
         Intent intent = new Intent("TEST_ACTION");
         intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION));
+        // Not an intent, don't count.
         intent.putExtra(TEST_EXTRA_NAME + "2", 1);
+        ArrayList<Intent> intents = new ArrayList<>();
+        intents.add(new Intent(TEST_ACTION));
+        intent.putParcelableArrayListExtra(TEST_EXTRA_NAME + "3", intents);
+        intent.setClipData(ClipData.newIntent("label", new Intent(TEST_ACTION)));
 
         intent.collectExtraIntentKeys();
         final Parcel parcel = Parcel.obtain();
@@ -68,7 +77,7 @@
 
         assertEquals(intent.getAction(), target.getAction());
         assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys());
-        assertThat(intent.getExtraIntentKeys()).hasSize(1);
+        assertThat(intent.getExtraIntentKeys()).hasSize(3);
     }
 
     @Test
@@ -87,13 +96,37 @@
     @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT)
     public void testCollectExtraIntentKeys() {
         Intent intent = new Intent(TEST_ACTION);
-        Intent extraIntent = new Intent(TEST_ACTION, TEST_URI);
-        intent.putExtra(TEST_EXTRA_NAME, extraIntent);
+
+        Intent[] intents = new Intent[10];
+        for (int i = 0; i < intents.length; i++) {
+            intents[i] = new Intent("action" + i);
+        }
+        Intent[] intents2 = new Intent[2]; // intents[6-7]
+        System.arraycopy(intents, 6, intents2, 0, intents2.length);
+        ArrayList<Intent> intents3 = new ArrayList<>(2);
+        intents3.addAll(Arrays.asList(intents).subList(8, 10)); // intents[8-9]
+        intent.putExtra("key1", intents[0]);
+        intent.putExtra("array-key", intents2);
+        intent.setClipData(ClipData.newIntent("label2", intents[1]));
+        intent.putExtra("intkey", 1);
+        intents[0].putExtra("key3", intents[2]);
+        intents[0].setClipData(ClipData.newIntent("label4", intents[3]));
+        intents[0].putParcelableArrayListExtra("array-list-key", intents3);
+        intents[1].putExtra("key3", intents[4]);
+        intents[1].setClipData(ClipData.newIntent("label4", intents[5]));
+        intents[5].putExtra("intkey", 2);
 
         intent.collectExtraIntentKeys();
 
-        assertThat(intent.getExtraIntentKeys()).hasSize(1);
-        assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME);
+        // collect all actions of nested intents.
+        final List<String> actions = new ArrayList<>();
+        intent.forEachNestedCreatorToken(intent1 -> {
+            actions.add(intent1.getAction());
+        });
+        assertThat(actions).hasSize(10);
+        for (int i = 0; i < intents.length; i++) {
+            assertThat(actions).contains("action" + i);
+        }
     }
 
 }
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 4c47de0..d55a71e 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1761,7 +1761,7 @@
 
         if (Flags.displayBt2020Colorspace()) {
             sNamedColorSpaceMap.put(Named.DISPLAY_BT2020.ordinal(), new ColorSpace.Rgb(
-                    "BT 2020",
+                    "Display BT. 2020",
                     BT2020_PRIMARIES,
                     ILLUMINANT_D65,
                     null,
diff --git a/libs/appfunctions/Android.bp b/libs/appfunctions/Android.bp
index c6cee07..5ab5a7a 100644
--- a/libs/appfunctions/Android.bp
+++ b/libs/appfunctions/Android.bp
@@ -18,10 +18,10 @@
 }
 
 java_sdk_library {
-    name: "com.google.android.appfunctions.sidecar",
+    name: "com.android.extensions.appfunctions",
     owner: "google",
     srcs: ["java/**/*.java"],
-    api_packages: ["com.google.android.appfunctions.sidecar"],
+    api_packages: ["com.android.extensions.appfunctions"],
     dex_preopt: {
         enabled: false,
     },
@@ -31,9 +31,9 @@
 }
 
 prebuilt_etc {
-    name: "appfunctions.sidecar.xml",
+    name: "appfunctions.extension.xml",
     system_ext_specific: true,
     sub_dir: "permissions",
-    src: "appfunctions.sidecar.xml",
+    src: "appfunctions.extension.xml",
     filename_from_src: true,
 }
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index faf84a8..0eda101 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -1,9 +1,9 @@
 // Signature format: 2.0
-package com.google.android.appfunctions.sidecar {
+package com.android.extensions.appfunctions {
 
   public final class AppFunctionManager {
     ctor public AppFunctionManager(android.content.Context);
-    method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.android.extensions.appfunctions.ExecuteAppFunctionResponse>);
     method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
@@ -15,7 +15,7 @@
   public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @MainThread public abstract void onExecuteFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.android.extensions.appfunctions.ExecuteAppFunctionResponse>);
     field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
@@ -29,9 +29,9 @@
 
   public static final class ExecuteAppFunctionRequest.Builder {
     ctor public ExecuteAppFunctionRequest.Builder(@NonNull String, @NonNull String);
-    method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest build();
-    method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle);
-    method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument);
+    method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest build();
+    method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument);
   }
 
   public final class ExecuteAppFunctionResponse {
@@ -41,13 +41,13 @@
     method public int getResultCode();
     method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
     method public boolean isSuccess();
-    method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
-    method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
+    method @NonNull public static com.android.extensions.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
+    method @NonNull public static com.android.extensions.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
     field public static final int ERROR_CATEGORY_APP = 3; // 0x3
     field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
     field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
     field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
-    field public static final String PROPERTY_RETURN_VALUE = "returnValue";
+    field public static final String PROPERTY_RETURN_VALUE = "android_app_appfunctions_returnvalue";
     field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
     field public static final int RESULT_CANCELLED = 2001; // 0x7d1
     field public static final int RESULT_DENIED = 1000; // 0x3e8
diff --git a/libs/appfunctions/appfunctions.sidecar.xml b/libs/appfunctions/appfunctions.extension.xml
similarity index 83%
rename from libs/appfunctions/appfunctions.sidecar.xml
rename to libs/appfunctions/appfunctions.extension.xml
index bef8b6e..dd09cc3 100644
--- a/libs/appfunctions/appfunctions.sidecar.xml
+++ b/libs/appfunctions/appfunctions.extension.xml
@@ -16,6 +16,6 @@
   -->
 <permissions>
     <library
-        name="com.google.android.appfunctions.sidecar"
-        file="/system_ext/framework/com.google.android.appfunctions.sidecar.jar"/>
+        name="com.android.extensions.appfunctions"
+        file="/system_ext/framework/com.android.extensions.appfunctions.jar"/>
 </permissions>
\ No newline at end of file
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
similarity index 98%
rename from libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
rename to libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
index 2075104..d64593d 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
similarity index 98%
rename from libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
rename to libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
index 0dc87e4..1a4d9da 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
 
 import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
 
@@ -26,7 +26,6 @@
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.IBinder;
-import android.util.Log;
 
 import java.util.function.Consumer;
 
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java
similarity index 96%
rename from libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
rename to libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java
index 593c521..baddc24 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
 
 import android.annotation.NonNull;
 import android.app.appsearch.GenericDocument;
@@ -91,8 +91,8 @@
      * Returns the function parameters. The key is the parameter name, and the value is the
      * parameter value.
      *
-     * <p>The bundle may have missing parameters. Developers are advised to implement defensive
-     * handling measures.
+     * <p>The {@link GenericDocument} may have missing parameters. Developers are advised to
+     * implement defensive handling measures.
      *
      * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be
      * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java
similarity index 98%
rename from libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
rename to libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java
index 4e88fb0..7c5ddcd 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -48,7 +48,7 @@
      *
      * <p>See {@link #getResultDocument} for more information on extracting the return value.
      */
-    public static final String PROPERTY_RETURN_VALUE = "returnValue";
+    public static final String PROPERTY_RETURN_VALUE = "android_app_appfunctions_returnvalue";
 
     /**
      * The call was successful.
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java b/libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java
similarity index 69%
rename from libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java
rename to libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java
index b1b05f7..56f2725f 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.appfunctions.sidecar;
+package com.android.extensions.appfunctions;
 
 import android.annotation.NonNull;
 
@@ -28,26 +28,24 @@
     private SidecarConverter() {}
 
     /**
-     * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest}
-     * into platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest}
+     * Converts sidecar's {@link ExecuteAppFunctionRequest} into platform's {@link
+     * android.app.appfunctions.ExecuteAppFunctionRequest}
      *
      * @hide
      */
     @NonNull
     public static android.app.appfunctions.ExecuteAppFunctionRequest
             getPlatformExecuteAppFunctionRequest(@NonNull ExecuteAppFunctionRequest request) {
-        return new
-                android.app.appfunctions.ExecuteAppFunctionRequest.Builder(
-                request.getTargetPackageName(),
-                request.getFunctionIdentifier())
+        return new android.app.appfunctions.ExecuteAppFunctionRequest.Builder(
+                        request.getTargetPackageName(), request.getFunctionIdentifier())
                 .setExtras(request.getExtras())
                 .setParameters(request.getParameters())
                 .build();
     }
 
     /**
-     * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse}
-     * into platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse}
+     * Converts sidecar's {@link ExecuteAppFunctionResponse} into platform's {@link
+     * android.app.appfunctions.ExecuteAppFunctionResponse}
      *
      * @hide
      */
@@ -59,15 +57,13 @@
                     response.getResultDocument(), response.getExtras());
         } else {
             return android.app.appfunctions.ExecuteAppFunctionResponse.newFailure(
-                    response.getResultCode(),
-                    response.getErrorMessage(),
-                    response.getExtras());
+                    response.getResultCode(), response.getErrorMessage(), response.getExtras());
         }
     }
 
     /**
-     * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest}
-     * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest}
+     * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest} into sidecar's
+     * {@link ExecuteAppFunctionRequest}
      *
      * @hide
      */
@@ -75,16 +71,15 @@
     public static ExecuteAppFunctionRequest getSidecarExecuteAppFunctionRequest(
             @NonNull android.app.appfunctions.ExecuteAppFunctionRequest request) {
         return new ExecuteAppFunctionRequest.Builder(
-                request.getTargetPackageName(),
-                request.getFunctionIdentifier())
+                        request.getTargetPackageName(), request.getFunctionIdentifier())
                 .setExtras(request.getExtras())
                 .setParameters(request.getParameters())
                 .build();
     }
 
     /**
-     * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse}
-     * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse}
+     * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse} into
+     * sidecar's {@link ExecuteAppFunctionResponse}
      *
      * @hide
      */
@@ -96,9 +91,7 @@
                     response.getResultDocument(), response.getExtras());
         } else {
             return ExecuteAppFunctionResponse.newFailure(
-                    response.getResultCode(),
-                    response.getErrorMessage(),
-                    response.getExtras());
+                    response.getResultCode(), response.getErrorMessage(), response.getExtras());
         }
     }
 }
diff --git a/libs/appfunctions/tests/Android.bp b/libs/appfunctions/tests/Android.bp
index 6f5eff3..db79675 100644
--- a/libs/appfunctions/tests/Android.bp
+++ b/libs/appfunctions/tests/Android.bp
@@ -25,7 +25,7 @@
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "androidx.core_core-ktx",
-        "com.google.android.appfunctions.sidecar.impl",
+        "com.android.extensions.appfunctions.impl",
         "junit",
         "kotlin-test",
         "mockito-target-extended-minus-junit4",
diff --git a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt b/libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt
similarity index 93%
rename from libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
rename to libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt
index 264f842..6118e6c 100644
--- a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
+++ b/libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.google.android.appfunctions.sidecar.tests
+package com.android.extensions.appfunctions.tests
 
 import android.app.appfunctions.ExecuteAppFunctionRequest
 import android.app.appfunctions.ExecuteAppFunctionResponse
 import android.app.appsearch.GenericDocument
 import android.os.Bundle
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.google.android.appfunctions.sidecar.SidecarConverter
+import com.android.extensions.appfunctions.SidecarConverter
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,7 +60,7 @@
                 .setPropertyLong("testLong", 23)
                 .build()
         val sidecarRequest =
-            com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder(
+            com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder(
                 "targetPkg",
                 "targetFunctionId"
             )
@@ -129,8 +129,11 @@
             GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                 .setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true)
                 .build()
-        val sidecarResponse = com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse
-            .newSuccess(resultGd, null)
+        val sidecarResponse =
+            com.android.extensions.appfunctions.ExecuteAppFunctionResponse.newSuccess(
+                resultGd,
+                null
+            )
 
         val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse(
             sidecarResponse
@@ -151,7 +154,7 @@
     fun getPlatformExecuteAppFunctionResponse_errorResponse_sameContents() {
         val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
         val sidecarResponse =
-            com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse.newFailure(
+            com.android.extensions.appfunctions.ExecuteAppFunctionResponse.newFailure(
                 ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
                 null,
                 null
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 6b371d7..9b47ead 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -19,6 +19,7 @@
 
 import android.app.admin.devicePolicyManager
 import android.content.pm.UserInfo
+import android.internal.statusbar.fakeStatusBarService
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
@@ -61,6 +62,7 @@
     private val globalSettings = kosmos.fakeGlobalSettings
     private val broadcastDispatcher = kosmos.broadcastDispatcher
     private val devicePolicyManager = kosmos.devicePolicyManager
+    private val statusBarService = kosmos.fakeStatusBarService
 
     @Mock private lateinit var manager: UserManager
 
@@ -323,6 +325,8 @@
             tracker = tracker,
             broadcastDispatcher = broadcastDispatcher,
             devicePolicyManager = devicePolicyManager,
+            resources = context.resources,
+            statusBarService = statusBarService,
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt
index 26439df..f70b426 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt
@@ -49,35 +49,78 @@
     @Before
     fun setUp() {
         userRepository.setUserInfos(USER_INFOS)
-        runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[1]) }
+        runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[2]) }
+        userRepository.setLogoutToSystemUserEnabled(false)
+        userRepository.setSecondaryUserLogoutEnabled(false)
     }
 
     @Test
-    fun logOut_doesNothing_whenAdminDisabledSecondaryLogout() {
+    fun logOut_doesNothing_whenBothLogoutOptionsAreDisabled() {
         testScope.runTest {
             val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
-            val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
-            userRepository.setSecondaryUserLogoutEnabled(false)
+            val secondaryUserLogoutCount = userRepository.logOutSecondaryUserCallCount
+            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
             assertThat(isLogoutEnabled).isFalse()
             underTest.logOut()
-            assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount)
+            assertThat(userRepository.logOutSecondaryUserCallCount)
+                .isEqualTo(secondaryUserLogoutCount)
+            assertThat(userRepository.logOutToSystemUserCallCount)
+                .isEqualTo(logoutToSystemUserCount)
         }
     }
 
     @Test
-    fun logOut_logsOut_whenAdminEnabledSecondaryLogout() {
+    fun logOut_logsOutSecondaryUser_whenAdminEnabledSecondaryLogout() {
         testScope.runTest {
             val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
             val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
+            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
             userRepository.setSecondaryUserLogoutEnabled(true)
             assertThat(isLogoutEnabled).isTrue()
             underTest.logOut()
             assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1)
+            assertThat(userRepository.logOutToSystemUserCallCount)
+                .isEqualTo(logoutToSystemUserCount)
+        }
+    }
+
+    @Test
+    fun logOut_logsOutToSystemUser_whenLogoutToSystemUserIsEnabled() {
+        testScope.runTest {
+            val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
+            val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
+            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
+            userRepository.setLogoutToSystemUserEnabled(true)
+            assertThat(isLogoutEnabled).isTrue()
+            underTest.logOut()
+            assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount)
+            assertThat(userRepository.logOutToSystemUserCallCount)
+                .isEqualTo(logoutToSystemUserCount + 1)
+        }
+    }
+
+    @Test
+    fun logOut_secondaryUserTakesPrecedence() {
+        testScope.runTest {
+            val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled)
+            val lastLogoutCount = userRepository.logOutSecondaryUserCallCount
+            val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount
+            userRepository.setLogoutToSystemUserEnabled(true)
+            userRepository.setSecondaryUserLogoutEnabled(true)
+            assertThat(isLogoutEnabled).isTrue()
+            underTest.logOut()
+            assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1)
+            assertThat(userRepository.logOutToSystemUserCallCount)
+                .isEqualTo(logoutToSystemUserCount)
         }
     }
 
     companion object {
         private val USER_INFOS =
-            listOf(UserInfo(0, "System user", 0), UserInfo(10, "Regular user", 0))
+            listOf(
+                UserInfo(0, "System user", 0),
+                UserInfo(10, "Regular user", 0),
+                UserInfo(11, "Secondary user", 0),
+            )
     }
 }
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 82c8c44..0854eb4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1086,4 +1086,9 @@
     enable the desktop specific features.
     -->
     <bool name="config_enableDesktopFeatureSet">false</bool>
+
+    <!--
+    Whether the user switching can only happen by logging out and going through the system user (login screen).
+    -->
+    <bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index f20ce63..e9a33e0 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -23,11 +23,13 @@
 import android.content.Context
 import android.content.IntentFilter
 import android.content.pm.UserInfo
+import android.content.res.Resources
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -109,6 +111,9 @@
     /** Whether logout for secondary users is enabled by admin device policy. */
     val isSecondaryUserLogoutEnabled: StateFlow<Boolean>
 
+    /** Whether logout into system user is enabled. */
+    val isLogoutToSystemUserEnabled: StateFlow<Boolean>
+
     /** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */
     fun refreshUsers()
 
@@ -121,6 +126,9 @@
     /** Performs logout logout for secondary users. */
     suspend fun logOutSecondaryUser()
 
+    /** Performs logout into the system user. */
+    suspend fun logOutToSystemUser()
+
     /**
      * Returns the user ID of the "main user" of the device. This user may have access to certain
      * features which are limited to at most one user. There will never be more than one main user
@@ -143,6 +151,7 @@
 @Inject
 constructor(
     @Application private val appContext: Context,
+    @Main private val resources: Resources,
     private val manager: UserManager,
     @Application private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
@@ -151,6 +160,7 @@
     private val tracker: UserTracker,
     private val devicePolicyManager: DevicePolicyManager,
     private val broadcastDispatcher: BroadcastDispatcher,
+    private val statusBarService: IStatusBarService,
 ) : UserRepository {
 
     private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
@@ -275,12 +285,34 @@
             .stateIn(applicationScope, SharingStarted.Eagerly, false)
 
     @SuppressLint("MissingPermission")
+    override val isLogoutToSystemUserEnabled: StateFlow<Boolean> =
+        selectedUser
+            .flatMapLatestConflated { selectedUser ->
+                if (selectedUser.isEligibleForLogout()) {
+                    flowOf(
+                        resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen)
+                    )
+                } else {
+                    flowOf(false)
+                }
+            }
+            .stateIn(applicationScope, SharingStarted.Eagerly, false)
+
+    @SuppressLint("MissingPermission")
     override suspend fun logOutSecondaryUser() {
         if (isSecondaryUserLogoutEnabled.value) {
             withContext(backgroundDispatcher) { devicePolicyManager.logoutUser() }
         }
     }
 
+    override suspend fun logOutToSystemUser() {
+        // TODO(b/377493351) : start using proper logout API once it is available.
+        // Using reboot is a temporary solution.
+        if (isLogoutToSystemUserEnabled.value) {
+            withContext(backgroundDispatcher) { statusBarService.reboot(false) }
+        }
+    }
+
     @SuppressLint("MissingPermission")
     override fun refreshUsers() {
         applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt
index 154f1dc..f2dd25f 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt
@@ -23,7 +23,10 @@
 import com.android.systemui.user.data.repository.UserRepository
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business logic to for the logout. */
 @SysUISingleton
@@ -33,11 +36,22 @@
     private val userRepository: UserRepository,
     @Application private val applicationScope: CoroutineScope,
 ) {
-    val isLogoutEnabled: StateFlow<Boolean> = userRepository.isSecondaryUserLogoutEnabled
+
+    val isLogoutEnabled: StateFlow<Boolean> =
+        combine(
+                userRepository.isSecondaryUserLogoutEnabled,
+                userRepository.isLogoutToSystemUserEnabled,
+                Boolean::or,
+            )
+            .stateIn(applicationScope, SharingStarted.Eagerly, false)
 
     fun logOut() {
-        if (userRepository.isSecondaryUserLogoutEnabled.value) {
-            applicationScope.launch { userRepository.logOutSecondaryUser() }
+        applicationScope.launch {
+            if (userRepository.isSecondaryUserLogoutEnabled.value) {
+                userRepository.logOutSecondaryUser()
+            } else if (userRepository.isLogoutToSystemUserEnabled.value) {
+                userRepository.logOutToSystemUser()
+            }
         }
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 1808a5f..85d582a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -72,6 +72,10 @@
     override val isSecondaryUserLogoutEnabled: StateFlow<Boolean> =
         _isSecondaryUserLogoutEnabled.asStateFlow()
 
+    private val _isLogoutToSystemUserEnabled = MutableStateFlow<Boolean>(false)
+    override val isLogoutToSystemUserEnabled: StateFlow<Boolean> =
+        _isLogoutToSystemUserEnabled.asStateFlow()
+
     override var mainUserId: Int = MAIN_USER_ID
     override var lastSelectedNonGuestUserId: Int = mainUserId
 
@@ -123,6 +127,17 @@
         logOutSecondaryUserCallCount++
     }
 
+    fun setLogoutToSystemUserEnabled(logoutEnabled: Boolean) {
+        _isLogoutToSystemUserEnabled.value = logoutEnabled
+    }
+
+    var logOutToSystemUserCallCount: Int = 0
+        private set
+
+    override suspend fun logOutToSystemUser() {
+        logOutToSystemUserCallCount++
+    }
+
     fun setUserInfos(infos: List<UserInfo>) {
         _userInfos.value = infos
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0826c53..18e8abb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19307,31 +19307,18 @@
     public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
         if (!preventIntentRedirect()) return;
 
-        if (intent == null || intent.getExtraIntentKeys() == null) return;
-        for (String key : intent.getExtraIntentKeys()) {
-            try {
-                Intent extraIntent = intent.getParcelableExtra(key, Intent.class);
-                if (extraIntent == null) {
-                    Slog.w(TAG, "The key {" + key
-                            + "} does not correspond to an intent in the extra bundle.");
-                    continue;
-                }
-                IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent,
-                        creatorPackage);
-                if (creatorToken != null) {
-                    extraIntent.setCreatorToken(creatorToken);
-                    Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
-                            + creatorPackage + "; intent: " + intent);
-                    FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED,
-                            creatorToken.getCreatorUid());
-                }
-            } catch (Exception e) {
-                Slog.wtf(TAG,
-                        "Something went wrong when trying to add creator token for embedded "
-                                + "intents of intent: ."
-                                + intent, e);
+        if (intent == null) return;
+        intent.forEachNestedCreatorToken(extraIntent -> {
+            IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
+            if (creatorToken != null) {
+                extraIntent.setCreatorToken(creatorToken);
+                // TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329
+                Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
+                        + creatorPackage + "; intent: " + intent);
+                FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED,
+                        creatorToken.getCreatorUid());
             }
-        }
+        });
     }
 
     private IntentCreatorToken createIntentCreatorToken(Intent intent, String creatorPackage) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index aa9ac6c..2eb9f3c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -124,6 +124,7 @@
 import com.android.server.Watchdog;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.power.feature.PowerManagerFlags;
 import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.BatteryExternalStatsWorker;
 import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
@@ -195,6 +196,7 @@
     private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
     private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
     private final PowerAttributor mPowerAttributor;
+    private final PowerManagerFlags mPowerManagerFlags = new PowerManagerFlags();
 
     private volatile boolean mMonitorEnabled = true;
     private boolean mRailsStatsCollectionEnabled = true;
@@ -617,6 +619,9 @@
                 BatteryConsumer.POWER_COMPONENT_ANY,
                 Flags.streamlinedMiscBatteryStats());
 
+        mStats.setMoveWscLoggingToNotifierEnabled(
+                mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled());
+
         mWorker.systemServicesReady();
         mStats.systemServicesReady(mContext);
         mCpuWakeupStats.systemServicesReady();
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 76e5ef0..0c04be1 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.service.dreams.Flags.cleanupDreamSettingsOnUninstall;
 import static android.service.dreams.Flags.dreamHandlesBeingObscured;
 
 import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
@@ -64,12 +65,15 @@
 import android.service.dreams.DreamManagerInternal;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.text.TextUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.util.DumpUtils;
@@ -86,6 +90,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -155,6 +160,10 @@
     private ComponentName mDreamOverlayServiceName;
 
     private final AmbientDisplayConfiguration mDozeConfig;
+
+    /** Stores {@link PerUserPackageMonitor} to monitor dream uninstalls. */
+    private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
+
     private final ActivityInterceptorCallback mActivityInterceptorCallback =
             new ActivityInterceptorCallback() {
                 @Nullable
@@ -218,6 +227,15 @@
         }
     }
 
+    private final class PerUserPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            super.onPackageRemoved(packageName, uid);
+            final int userId = getChangingUserId();
+            updateDreamOnPackageRemoved(packageName, userId);
+        }
+    }
+
     public DreamManagerService(Context context) {
         this(context, new DreamHandler(FgThread.get().getLooper()));
     }
@@ -333,6 +351,37 @@
         });
     }
 
+    @Override
+    public void onUserStarting(@NonNull TargetUser user) {
+        super.onUserStarting(user);
+        if (cleanupDreamSettingsOnUninstall()) {
+            mHandler.post(() -> {
+                final int userId = user.getUserIdentifier();
+                if (!mPackageMonitors.contains(userId)) {
+                    final PackageMonitor monitor = new PerUserPackageMonitor();
+                    monitor.register(mContext, UserHandle.of(userId), mHandler);
+                    mPackageMonitors.put(userId, monitor);
+                } else {
+                    Slog.w(TAG, "Package monitor already registered for " + userId);
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onUserStopping(@NonNull TargetUser user) {
+        super.onUserStopping(user);
+        if (cleanupDreamSettingsOnUninstall()) {
+            mHandler.post(() -> {
+                final PackageMonitor monitor = mPackageMonitors.removeReturnOld(
+                        user.getUserIdentifier());
+                if (monitor != null) {
+                    monitor.unregister();
+                }
+            });
+        }
+    }
+
     private void dumpInternal(PrintWriter pw) {
         synchronized (mLock) {
             pw.println("DREAM MANAGER (dumpsys dreams)");
@@ -664,6 +713,30 @@
         return validComponents.toArray(new ComponentName[validComponents.size()]);
     }
 
+    private void updateDreamOnPackageRemoved(String packageName, int userId) {
+        final ComponentName[] componentNames = componentsFromString(
+                Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.SCREENSAVER_COMPONENTS,
+                        userId));
+        if (componentNames != null) {
+            // Filter out any components in the removed package.
+            final ComponentName[] filteredComponents =
+                    Arrays.stream(componentNames)
+                            .filter((componentName -> !isSamePackage(packageName, componentName)))
+                            .toArray(ComponentName[]::new);
+            if (filteredComponents.length != componentNames.length) {
+                setDreamComponentsForUser(userId, filteredComponents);
+            }
+        }
+    }
+
+    private static boolean isSamePackage(String packageName, ComponentName componentName) {
+        if (packageName == null || componentName == null) {
+            return false;
+        }
+        return TextUtils.equals(componentName.getPackageName(), packageName);
+    }
+
     private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
         Settings.Secure.putStringForUser(mContext.getContentResolver(),
                 Settings.Secure.SCREENSAVER_COMPONENTS,
@@ -824,7 +897,10 @@
         }
         StringBuilder names = new StringBuilder();
         for (ComponentName componentName : componentNames) {
-            if (names.length() > 0) {
+            if (componentName == null) {
+                continue;
+            }
+            if (!names.isEmpty()) {
                 names.append(',');
             }
             names.append(componentName.flattenToString());
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index bf415a3..7505c71 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -646,9 +646,9 @@
         int address = message.getSource();
         int type = message.getParams()[2];
 
-        if (!ActiveSource.of(address, path).equals(getActiveSource())) {
-            HdmiLogger.debug("Check if a new device is connected to the active path");
-            handleNewDeviceAtTheTailOfActivePath(path);
+        if (getActiveSource().logicalAddress != address && getActivePath() == path) {
+            HdmiLogger.debug("New logical address detected on the current active path.");
+            startRoutingControl(path, path, null);
         }
         startNewDeviceAction(ActiveSource.of(address, path), type);
         return Constants.HANDLED;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 132d6fa..0c5069f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -771,6 +771,14 @@
             Slog.i(TAG, "Device does not support eARC.");
         }
         mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
+        if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) {
+            Slog.w(TAG, "Re-enable CEC on boot-up since it was disabled due to low energy "
+                    + " mode.");
+            getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+                    HDMI_CEC_CONTROL_ENABLED);
+            setWasCecDisabledOnStandbyByLowEnergyMode(false);
+            setCecEnabled(HDMI_CEC_CONTROL_ENABLED);
+        }
         if (isCecControlEnabled()) {
             initializeCec(INITIATED_BY_BOOT_UP);
         } else {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 677a2de..028ac57 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -303,6 +303,8 @@
     private final GnssPowerStatsCollector mGnssPowerStatsCollector;
     private final CustomEnergyConsumerPowerStatsCollector mCustomEnergyConsumerPowerStatsCollector;
     private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
+    private boolean mMoveWscLoggingToNotifierEnabled = false;
+
     private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever =
             new ScreenPowerStatsCollector.ScreenUsageTimeRetriever() {
 
@@ -5155,7 +5157,7 @@
 
             Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs);
             uidStats.noteStartWakeLocked(pid, name, type, elapsedRealtimeMs);
-            if (!mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()) {
+            if (!mMoveWscLoggingToNotifierEnabled) {
                 mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
                         uidStats.mProcessState, true /* acquired */,
                         getPowerManagerWakeLockLevel(type));
@@ -5206,7 +5208,7 @@
             Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs);
             uidStats.noteStopWakeLocked(pid, name, type, elapsedRealtimeMs);
 
-            if (!mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()) {
+            if (!mMoveWscLoggingToNotifierEnabled) {
                 mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
                         uidStats.mProcessState, false/* acquired */,
                         getPowerManagerWakeLockLevel(type));
@@ -15975,6 +15977,15 @@
         }
     }
 
+    /**
+     * Controls where the logging of the WakelockStateChanged atom occurs:
+     *   true = Notifier, false = BatteryStatsImpl.
+     */
+    public void setMoveWscLoggingToNotifierEnabled(boolean enabled) {
+        synchronized (this) {
+            mMoveWscLoggingToNotifierEnabled = enabled;
+        }
+    }
     @GuardedBy("this")
     public void systemServicesReady(Context context) {
         mConstants.startObserving(context.getContentResolver());
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 73ae51c..14be59f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -254,6 +254,7 @@
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -10308,6 +10309,21 @@
         if (pictureInPictureArgs != null && pictureInPictureArgs.hasSourceBoundsHint()) {
             pictureInPictureArgs.getSourceRectHint().offset(windowBounds.left, windowBounds.top);
         }
+
+        if (android.app.Flags.enableTvImplicitEnterPipRestriction()) {
+            PackageManager pm = mAtmService.mContext.getPackageManager();
+            if (pictureInPictureArgs.isAutoEnterEnabled()
+                    && pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                    && pm.checkPermission(Manifest.permission.TV_IMPLICIT_ENTER_PIP, packageName)
+                    == PackageManager.PERMISSION_DENIED) {
+                Log.i(TAG,
+                        "Auto-enter PiP only allowed on TV if android.permission"
+                                + ".TV_IMPLICIT_ENTER_PIP permission is held by the app.");
+                PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
+                builder.setAutoEnterEnabled(false);
+                pictureInPictureArgs.copyOnlySet(builder.build());
+            }
+        }
     }
 
     private void applyLocaleOverrideIfNeeded(Configuration resolvedConfig) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2a825f3..dcbc234 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -1308,6 +1308,8 @@
         Intent intent = new Intent();
         Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
         intent.putExtra("EXTRA_INTENT0", extraIntent);
+        Intent nestedIntent = new Intent("NESTED_INTENT_ACTION");
+        extraIntent.putExtra("NESTED_INTENT", nestedIntent);
 
         intent.collectExtraIntentKeys();
         mAms.addCreatorToken(intent, TEST_PACKAGE);
@@ -1317,6 +1319,11 @@
         assertThat(token).isNotNull();
         assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
         assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
+
+        token = (ActivityManagerService.IntentCreatorToken) nestedIntent.getCreatorToken();
+        assertThat(token).isNotNull();
+        assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+        assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
     }
 
     @Test
@@ -1349,6 +1356,8 @@
         Intent intent = new Intent();
         Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
         intent.putExtra("EXTRA_INTENT", extraIntent);
+        Intent nestedIntent = new Intent("NESTED_INTENT_ACTION");
+        extraIntent.putExtra("NESTED_INTENT", nestedIntent);
 
         intent.collectExtraIntentKeys();
 
@@ -1374,9 +1383,12 @@
         extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class);
         extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class);
         extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class);
+        nestedIntent = extraIntent.getParcelableExtra("NESTED_INTENT", Intent.class);
 
         assertThat(extraIntent.getExtendedFlags()
                 & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
+        assertThat(nestedIntent.getExtendedFlags()
+                & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
         // sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set.
         assertThat(extraIntent2.getExtendedFlags()
                 & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0);