Merge "Fix wrong mapping of Brazilian KeyCharacterMap" into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 43d2ae9..f47766e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -1335,13 +1335,13 @@
     private void handleOpTimeoutLocked() {
         switch (mVerb) {
             case VERB_BINDING:
-                onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
+                // The system may have been too busy. Don't drop the job or trigger an ANR.
+                onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true,
                         /* texCounterMetricId */
                         "job_scheduler.value_cntr_w_uid_slow_app_response_binding",
                         /* debugReason */ "timed out while binding",
                         /* anrMessage */ "Timed out while trying to bind",
-                        CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
-                            mRunningJob.getUid()));
+                        /* triggerAnr */ false);
                 break;
             case VERB_STARTING:
                 // Client unresponsive - wedged or failed to respond in time. We don't really
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index db751a4..aa48451 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1903,7 +1903,7 @@
     method public android.media.PlaybackParams setAudioStretchMode(int);
   }
 
-  public final class RingtoneSelection {
+  @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection {
     method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int);
     method public int getSoundSource();
     method @Nullable public android.net.Uri getSoundUri();
@@ -1915,14 +1915,16 @@
     field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3
     field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1
     field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2
-    field public static final int SOUND_SOURCE_DEFAULT = 0; // 0x0
     field public static final int SOUND_SOURCE_OFF = 1; // 0x1
+    field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
+    field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0
     field public static final int SOUND_SOURCE_URI = 2; // 0x2
-    field public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; // 0x3
+    field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4
     field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa
-    field public static final int VIBRATION_SOURCE_DEFAULT = 0; // 0x0
     field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb
     field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1
+    field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
+    field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0
     field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
   }
 
@@ -2610,17 +2612,17 @@
 
 package android.os.vibrator.persistence {
 
-  public class ParsedVibration {
+  @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public class ParsedVibration {
     method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects();
     method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
   }
 
-  public final class VibrationXmlParser {
+  @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlParser {
     method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException;
     method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException;
   }
 
-  public final class VibrationXmlSerializer {
+  @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlSerializer {
     method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException;
   }
 
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 107be8b..93e39d5 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -191,8 +191,14 @@
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
 UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED
 UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
@@ -203,6 +209,10 @@
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED:
+    New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED
 UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
     New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
 UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ca10d14..1aa8ebe 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1486,13 +1486,13 @@
             AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
 
     /**
-     * Allows the assistant app to get the training data from the PCC sandbox to improve the
+     * Allows the assistant app to get the training data from the trusted process to improve the
      * hotword training model.
      *
      * @hide
      */
-    public static final int OP_RECEIVE_SANDBOX_TRAINING_DATA =
-            AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRAINING_DATA;
+    public static final int OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA =
+            AppProtoEnums.APP_OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA;
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1640,7 +1640,7 @@
             OPSTR_CAMERA_SANDBOXED,
             OPSTR_RECORD_AUDIO_SANDBOXED,
             OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
-            OPSTR_RECEIVE_SANDBOX_TRAINING_DATA
+            OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA
     })
     public @interface AppOpString {}
 
@@ -2261,13 +2261,13 @@
             "android:receive_sandbox_trigger_audio";
 
     /**
-     * Allows the assistant app to get the training data from the PCC sandbox to improve
+     * Allows the assistant app to get the training data from the trusted process to improve
      * the hotword training model.
      *
      * @hide
      */
-    public static final String OPSTR_RECEIVE_SANDBOX_TRAINING_DATA =
-            "android:receive_sandbox_training_data";
+    public static final String OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA =
+            "android:receive_trusted_process_training_data";
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -2811,9 +2811,9 @@
                 OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
                 "RECEIVE_SANDBOX_TRIGGER_AUDIO")
                 .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
-        new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRAINING_DATA,
-                OPSTR_RECEIVE_SANDBOX_TRAINING_DATA,
-                "RECEIVE_SANDBOX_TRAINING_DATA").build()
+        new AppOpInfo.Builder(OP_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA,
+                OPSTR_RECEIVE_TRUSTED_PROCESS_TRAINING_DATA,
+                "RECEIVE_TRUSTED_PROCESS_TRAINING_DATA").build()
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index d8e60c8..88f62f3 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -19,4 +19,11 @@
     name: "haptics_customization_ringtone_v2_enabled"
     description: "Enables the usage of the new RingtoneV2 class"
     bug: "241918098"
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "haptics"
+    name: "enable_vibration_serialization_apis"
+    description: "Enables the APIs for vibration serialization/deserialization."
+    bug: "245129509"
+}
diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java
index ded74ea..3d1deea 100644
--- a/core/java/android/os/vibrator/persistence/ParsedVibration.java
+++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java
@@ -16,6 +16,7 @@
 
 package android.os.vibrator.persistence;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -34,6 +35,7 @@
  *
  * @hide
  */
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
 @TestApi
 public class ParsedVibration {
     private final List<VibrationEffect> mEffects;
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index e08cc42..fed1053 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -16,6 +16,7 @@
 
 package android.os.vibrator.persistence;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -115,6 +116,7 @@
  *
  * @hide
  */
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
 @TestApi
 public final class VibrationXmlParser {
     private static final String TAG = "VibrationXmlParser";
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index 1cdfa4f..2880454 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -16,6 +16,7 @@
 
 package android.os.vibrator.persistence;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
@@ -42,6 +43,7 @@
  *
  * @hide
  */
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
 @TestApi
 public final class VibrationXmlSerializer {
 
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
index 74f7276..b74b6a3 100644
--- a/media/java/android/media/RingtoneSelection.java
+++ b/media/java/android/media/RingtoneSelection.java
@@ -18,16 +18,23 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.net.Uri;
+import android.os.UserHandle;
+import android.os.vibrator.Flags;
 import android.provider.MediaStore;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Immutable representation a desired ringtone, usually originating from a user preference.
@@ -46,7 +53,7 @@
  * to be internally consistent and reflect effective values - with the exception of not verifying
  * the actual URI content. For example, loading a selection Uri that sets a sound source to
  * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class
- * instead returning {@link #SOUND_SOURCE_DEFAULT} from {@link #getSoundSource}.
+ * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}.
  *
  * <h2>Storing preferences</h2>
  *
@@ -57,6 +64,7 @@
  * @hide
  */
 @TestApi
+@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
 public final class RingtoneSelection {
 
     /**
@@ -70,7 +78,7 @@
      * The sound source is not explicitly specified, so it can follow default behavior for its
      * context.
      */
-    public static final int SOUND_SOURCE_DEFAULT = 0;
+    public static final int SOUND_SOURCE_UNSPECIFIED = 0;
 
     /**
      * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker.
@@ -83,15 +91,25 @@
     public static final int SOUND_SOURCE_URI = 2;
 
     /**
+     * The sound should explicitly use the system default.
+     *
+     * <p>This value isn't valid within the system default itself.
+     */
+    public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3;
+
+    // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT.
+
+    /**
      * Directive for how to make sound.
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "SOUND_SOURCE_", value = {
             SOUND_SOURCE_UNKNOWN,
-            SOUND_SOURCE_DEFAULT,
+            SOUND_SOURCE_UNSPECIFIED,
             SOUND_SOURCE_OFF,
             SOUND_SOURCE_URI,
+            SOUND_SOURCE_SYSTEM_DEFAULT,
     })
     public @interface SoundSource {}
 
@@ -106,9 +124,9 @@
     /**
      * Vibration source is not explicitly specified. If vibration is enabled, this will use the
      * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL},
-     * {@link #VIBRATION_SOURCE_APPLICATION_PROVIDED}, or system default vibration.
+     * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      */
-    public static final int VIBRATION_SOURCE_DEFAULT = 0;
+    public static final int VIBRATION_SOURCE_UNSPECIFIED = 0;
 
     /** Specifies that vibration is explicitly disabled for this ringtone. */
     public static final int VIBRATION_SOURCE_OFF = 1;
@@ -117,22 +135,31 @@
     public static final int VIBRATION_SOURCE_URI = 2;
 
     /**
+     * The vibration should explicitly use the system default.
+     *
+     * <p>This value isn't valid within the system default itself.
+     */
+    public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3;
+
+    /**
      * Specifies that vibration should use the vibration provided by the application. This is
      * typically the application's own default for the use-case, provided via
      * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration
      * effect saved on the notification channel.
      *
      * <p>If no vibration is specified by the application, this value behaves if the source was
-     * {@link #VIBRATION_SOURCE_DEFAULT}.
+     * {@link #VIBRATION_SOURCE_UNSPECIFIED}.
+     *
+     * <p>This value isn't valid within the system default.
      */
-    public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3;
+    public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4;
 
     /**
      * Specifies that vibration should use haptic audio channels from the
      * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified
-     * by {@link #VIBRATION_SOURCE_DEFAULT}.
+     * by {@link #VIBRATION_SOURCE_UNSPECIFIED}.
      */
-    // Numeric gap from VIBRATION_SOURCE_APPLICATION_PROVIDED in case we want other common elements.
+    // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements.
     public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10;
 
     /**
@@ -151,10 +178,10 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "VIBRATION_SOURCE_", value = {
             VIBRATION_SOURCE_UNKNOWN,
-            VIBRATION_SOURCE_DEFAULT,
+            VIBRATION_SOURCE_UNSPECIFIED,
             VIBRATION_SOURCE_OFF,
             VIBRATION_SOURCE_URI,
-            VIBRATION_SOURCE_APPLICATION_PROVIDED,
+            VIBRATION_SOURCE_APPLICATION_DEFAULT,
             VIBRATION_SOURCE_AUDIO_CHANNEL,
             VIBRATION_SOURCE_HAPTIC_GENERATOR,
     })
@@ -162,9 +189,12 @@
 
     /**
      * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri
-     * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}.
-     * This behavior is particularly suited to loading values from older settings that may contain
-     * a raw sound Uri or null for silent.
+     * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF},
+     * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning
+     * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}.
+     *
+     * <p>This behavior is particularly suited to loading values from older settings that may
+     * contain a raw sound Uri or null for silent.
      *
      * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
      */
@@ -173,7 +203,8 @@
     /**
      * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration
      * Uri for the returned {@link RingtoneSelection}, with null meaning
-     * {@link #VIBRATION_SOURCE_OFF}.
+     * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs
+     * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      *
      * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
      */
@@ -182,7 +213,9 @@
     /**
      * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid
      * value. Null or an invalid values will revert to default behavior correspnoding to
-     * {@link #DEFAULT_SELECTION_URI_STRING}.
+     * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs
+     * ({@link RingtoneManager#getDefaultUri}) will set both
+     * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      *
      * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false,
      * which include {@code null}.
@@ -218,10 +251,11 @@
 
     /* Common param values */
     private static final String SOURCE_OFF_STRING = "off";
+    private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys";
 
     /* Vibration source param values. */
     private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac";
-    private static final String VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING = "app";
+    private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app";
     private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg";
 
     @Nullable
@@ -236,53 +270,45 @@
 
     private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource,
             @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) {
-        // Enforce guarantees on the source values: revert to unset if they depend on something
-        // that's not set.
-        switch (soundSource) {
-            case SOUND_SOURCE_URI:
-            case SOUND_SOURCE_UNKNOWN:  // Allow unknown to revert to URI before default.
-                mSoundSource = soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_DEFAULT;
-                break;
-            default:
-                mSoundSource = soundSource;
-                break;
-        }
-        switch (vibrationSource) {
-            case VIBRATION_SOURCE_AUDIO_CHANNEL:
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR:
-                mVibrationSource = soundUri != null ? vibrationSource : VIBRATION_SOURCE_DEFAULT;
-                break;
-            case VIBRATION_SOURCE_URI:
-            case VIBRATION_SOURCE_UNKNOWN:  // Allow unknown to revert to URI.
-                mVibrationSource =
-                        vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_DEFAULT;
-                break;
-            default:
-                mVibrationSource = vibrationSource;
-                break;
-        }
+        // Enforce guarantees on the source values: revert to unspecified if they depend on
+        // something that's not set.
+        //
+        // The non-public "unknown" value can't appear in a getter result, it's just a reserved
+        // "null" value and should be treated the same as an unrecognized value. This can be seen
+        // in Uri parsing. For this and other unrecognized values, we either revert them to the URI
+        // source, if a Uri was included, or the "unspecified" source otherwise. This can be
+        // seen in action in the Uri parsing.
+        //
+        // The "unspecified" source is a public value meaning that there is no specific
+        // behavior indicated, and the defaults and fallbacks should be applied. For example, an
+        // vibration source value of "system default" means to explicitly use the system default
+        // vibration. However, an "unspecified" vibration source will first see if audio coupled
+        // or application-default vibrations are available.
+        mSoundSource = switch (soundSource) {
+            // Supported explicit values that don't have a Uri.
+            case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT ->
+                    soundSource;
+            // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
+            // unspecified.
+            default ->
+                soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED;
+        };
+        mVibrationSource = switch (vibrationSource) {
+            // Enforce vibration sources that require a sound Uri.
+            case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR ->
+                    soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED;
+            // Supported explicit values that don't rely on any Uri.
+            case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED,
+                    VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT ->
+                    vibrationSource;
+            // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
+            // unspecified.
+            default ->
+                    vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED;
+        };
         // Clear Uri values if they're un-used by the source.
-        switch (mSoundSource) {
-            case SOUND_SOURCE_OFF:
-                mSoundUri = null;
-                break;
-            default:
-                // Unset case isn't handled here: the defaulting behavior is left to the player.
-                mSoundUri = soundUri;
-                break;
-        }
-        switch (mVibrationSource) {
-            case VIBRATION_SOURCE_OFF:
-            case VIBRATION_SOURCE_APPLICATION_PROVIDED:
-            case VIBRATION_SOURCE_AUDIO_CHANNEL:
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR:
-                mVibrationUri = null;
-                break;
-            default:
-                // Unset case isn't handled here: the defaulting behavior is left to the player.
-                mVibrationUri = vibrationUri;
-                break;
-        }
+        mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null;
+        mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null;
     }
 
     /**
@@ -360,18 +386,83 @@
         }
         // Any URI content://media/ringtone
         return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
-                && MediaStore.AUTHORITY.equals(uri.getAuthority())
+                && MediaStore.AUTHORITY.equals(
+                        ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()))
                 && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath());
     }
 
+    /**
+     * Strip the specified userId from internal Uris. Non-stripped userIds will typically be
+     * for work profiles referencing system ringtones from the host user.
+     *
+     * This is only for use in RingtoneManager.
+     * @hide
+     */
+    @VisibleForTesting
+    public RingtoneSelection getWithoutUserId(int userIdToStrip) {
+        if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) {
+            return this;
+        }
 
+        // Ok if uri is null. We only replace explicit references to the specified (current) userId.
+        int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL);
+        int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL);
+        boolean needToChangeSound =
+                soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip;
+        boolean needToChangeVibration =
+                vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip;
+
+        // Anything to do?
+        if (!needToChangeSound && !needToChangeVibration) {
+            return this;
+        }
+
+        RingtoneSelection.Builder updated = new Builder(this);
+        // The relevant uris can't be null, because they contain userIdToStrip.
+        if (needToChangeSound) {
+            // mSoundUri is not null, so the result of getUriWithoutUserId won't be null.
+            updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri));
+        }
+        if (needToChangeVibration) {
+            updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri));
+        }
+        return updated.build();
+    }
+
+    @Override
+    public String toString() {
+        return toUri().toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof RingtoneSelection other)) {
+            return false;
+        }
+        return this.mSoundSource == other.mSoundSource
+                && this.mVibrationSource == other.mVibrationSource
+                && Objects.equals(this.mSoundUri, other.mSoundUri)
+                && Objects.equals(this.mVibrationUri, other.mVibrationUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri);
+    }
 
     /**
      * Converts a Uri into a RingtoneSelection.
      *
-     * <p>Null values and Uris that {@link #isRingtoneSelectionUri(Uri)} returns false will be
-     * treated according to the behaviour specified by the {@code unrecognizedValueBehavior}
-     * parameter.
+     * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for
+     * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated
+     * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter.
+     *
+     * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1,
+     * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and
+     * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
      *
      * @param uri The Uri to convert, potentially null.
      * @param unrecognizedValueBehavior indicates how to treat values for which
@@ -384,21 +475,35 @@
         if (isRingtoneSelectionUri(uri)) {
             return parseRingtoneSelectionUri(uri);
         }
+        // Old symbolic default URIs map to the sources suggested by the unrecognized behavior.
+        // It doesn't always map to both sources because the app may have its own default behavior
+        // to apply, so non-primary sources are left as unspecified, which will revert to the
+        // system default in the absence of an app default.
+        boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0;
         RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
         switch (unrecognizedValueBehavior) {
             case FROM_URI_RINGTONE_SELECTION_ONLY:
-                // Always return use-defaults for unrecognized ringtone selection Uris.
+                if (isDefaultUri) {
+                    builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
+                    builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT);
+                }
+                // Always return unspecified (defaults) for unrecognized ringtone selection Uris.
                 return builder.build();
             case FROM_URI_RINGTONE_SELECTION_OR_SOUND:
                 if (uri == null) {
                     return builder.setSoundSource(SOUND_SOURCE_OFF).build();
+                } else if (isDefaultUri) {
+                    return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build();
                 } else {
                     return builder.setSoundSource(uri).build();
                 }
             case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
                 if (uri == null) {
                     return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build();
+                } else if (isDefaultUri) {
+                    return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build();
                 } else {
+                    // Unlike sound, there's no legacy settings alias uri for the default.
                     return builder.setVibrationSource(uri).build();
                 }
             default:
@@ -407,7 +512,12 @@
         }
     }
 
-    /** Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. */
+    /**
+     * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}.
+     * As a special case to be more compatible, if the RingtoneSelection has a userId specified in
+     * the authority, then this is pushed down into the sound and vibration Uri, as the selection
+     * itself is agnostic.
+     */
     @NonNull
     private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) {
         RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
@@ -416,19 +526,39 @@
                 uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE));
         Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI);
         Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI);
+
+        // Add userId if necessary. This won't override an existing one in the sound/vib Uris.
+        int userIdFromAuthority = ContentProvider.getUserIdFromAuthority(
+                uri.getAuthority(), UserHandle.USER_NULL);
+        if (userIdFromAuthority != UserHandle.USER_NULL) {
+            // Won't override existing user id.
+            if (soundUri != null) {
+                soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority);
+            }
+            if (vibrationUri != null) {
+                vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority);
+            }
+        }
+
+        // Set sound uri if present, but map system default Uris to the system default source.
         if (soundUri != null) {
-            builder.setSoundSource(soundUri);
+            if (RingtoneManager.getDefaultType(uri) >= 0) {
+                builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
+            } else {
+                builder.setSoundSource(soundUri);
+            }
         }
         if (vibrationUri != null) {
             builder.setVibrationSource(vibrationUri);
         }
+
         // Don't set the source if there's a URI and the source is default, because that will
         // override the Uri source set above. In effect, we prioritise "explicit" sources over
         // an implicit Uri source - except for "default", which isn't really explicit.
-        if (soundSource != SOUND_SOURCE_DEFAULT || soundUri == null) {
+        if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) {
             builder.setSoundSource(soundSource);
         }
-        if (vibrationSource != VIBRATION_SOURCE_DEFAULT || vibrationUri == null) {
+        if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) {
             builder.setVibrationSource(vibrationSource);
         }
         return builder.build();
@@ -450,26 +580,28 @@
      */
     @Nullable
     private static String soundSourceToString(@SoundSource int soundSource) {
-        switch (soundSource) {
-            case SOUND_SOURCE_OFF: return SOURCE_OFF_STRING;
-            default: return null;
-        }
+        return switch (soundSource) {
+            case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING;
+            case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
+            default -> null;
+        };
     }
 
     /**
      * Returns the sound source int corresponding to the query string value. Returns
      * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and
-     * {@link #SOUND_SOURCE_DEFAULT} if the value is {@code null} (not in the Uri).
+     * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri).
      */
     @SoundSource
     private static int stringToSoundSource(@Nullable String soundSource) {
         if (soundSource == null) {
-            return SOUND_SOURCE_DEFAULT;
+            return SOUND_SOURCE_UNSPECIFIED;
         }
-        switch (soundSource) {
-            case SOURCE_OFF_STRING: return SOUND_SOURCE_OFF;
-            default: return SOUND_SOURCE_UNKNOWN;
-        }
+        return switch (soundSource) {
+            case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF;
+            case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT;
+            default -> SOUND_SOURCE_UNKNOWN;
+        };
     }
 
     /**
@@ -478,30 +610,31 @@
      */
     @Nullable
     private static String vibrationSourceToString(@VibrationSource int vibrationSource) {
-        switch (vibrationSource) {
-            case VIBRATION_SOURCE_OFF: return SOURCE_OFF_STRING;
-            case VIBRATION_SOURCE_AUDIO_CHANNEL: return VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR:
-                return VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
-            case VIBRATION_SOURCE_APPLICATION_PROVIDED:
-                return VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING;
-            default: return null;
-        }
+        return switch (vibrationSource) {
+            case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING;
+            case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
+            case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
+            case VIBRATION_SOURCE_APPLICATION_DEFAULT ->
+                    VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING;
+            case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
+            default -> null;
+        };
     }
 
     @VibrationSource
     private static int stringToVibrationSource(@Nullable String vibrationSource) {
         if (vibrationSource == null) {
-            return VIBRATION_SOURCE_DEFAULT;
+            return VIBRATION_SOURCE_UNSPECIFIED;
         }
-        switch (vibrationSource) {
-            case SOURCE_OFF_STRING: return VIBRATION_SOURCE_OFF;
-            case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING: return VIBRATION_SOURCE_AUDIO_CHANNEL;
-            case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING: return VIBRATION_SOURCE_HAPTIC_GENERATOR;
-            case VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING:
-                return VIBRATION_SOURCE_APPLICATION_PROVIDED;
-            default: return VIBRATION_SOURCE_UNKNOWN;
-        }
+        return switch (vibrationSource) {
+            case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF;
+            case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT;
+            case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL;
+            case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR;
+            case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING ->
+                    VIBRATION_SOURCE_APPLICATION_DEFAULT;
+            default -> VIBRATION_SOURCE_UNKNOWN;
+        };
     }
 
     /**
@@ -512,12 +645,13 @@
     public static final class Builder {
         private Uri mSoundUri;
         private Uri mVibrationUri;
-        @SoundSource private int mSoundSource = SOUND_SOURCE_DEFAULT;
-        @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_DEFAULT;
+        @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED;
+        @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED;
 
         /**
          * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its
-         * sound and vibration source unset, which means they would fall back to system defaults.
+         * sound and vibration source unspecified, which means they would fall back to app/system
+         * defaults.
          */
         public Builder() {}
 
@@ -559,7 +693,9 @@
          */
         @NonNull
         public Builder setSoundSource(@NonNull Uri soundUri) {
-            mSoundUri = requireNonNull(soundUri);
+            // getCanonicalUri shouldn't return null. If it somehow did, then the
+            // RingtoneSelection constructor will revert this to unspecified.
+            mSoundUri = requireNonNull(soundUri).getCanonicalUri();
             mSoundSource = SOUND_SOURCE_URI;
             return this;
         }
@@ -587,7 +723,9 @@
          */
         @NonNull
         public Builder setVibrationSource(@NonNull Uri vibrationUri) {
-            mVibrationUri = requireNonNull(vibrationUri);
+            // getCanonicalUri shouldn't return null. If it somehow did, then the
+            // RingtoneSelection constructor will revert this to unspecified.
+            mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri();
             mVibrationSource = VIBRATION_SOURCE_URI;
             return this;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 51c0676..50be97e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -139,7 +139,7 @@
             case PROMPT_REASON_USER_REQUEST:
                 return R.string.kg_prompt_after_user_lockdown_password;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                return R.string.kg_prompt_unattended_update_password;
+                return R.string.kg_prompt_reason_timeout_password;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
                 return R.string.kg_prompt_reason_timeout_password;
             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 714ba81..57151ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -321,7 +321,7 @@
                 resId = R.string.kg_prompt_after_user_lockdown_pattern;
                 break;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                resId = R.string.kg_prompt_unattended_update_pattern;
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
                 resId = R.string.kg_prompt_reason_timeout_pattern;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 9d6d033..681aa70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -123,7 +123,7 @@
             case PROMPT_REASON_USER_REQUEST:
                 return R.string.kg_prompt_after_user_lockdown_pin;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                return R.string.kg_prompt_unattended_update_pin;
+                return R.string.kg_prompt_reason_timeout_pin;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
                 return R.string.kg_prompt_reason_timeout_pin;
             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index d6987629..ac012f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -29,8 +29,10 @@
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Handles key events arriving when the keyguard is showing or device is dozing. */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class KeyguardKeyEventInteractor
 @Inject
@@ -56,7 +58,11 @@
         if (event.handleAction()) {
             when (event.keyCode) {
                 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
-                KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
+                KeyEvent.KEYCODE_SPACE,
+                KeyEvent.KEYCODE_ENTER ->
+                    if (isDeviceAwake()) {
+                        return collapseShadeLockedOrShowPrimaryBouncer()
+                    }
             }
         }
         return false
@@ -92,16 +98,24 @@
                 (statusBarStateController.state != StatusBarState.SHADE) &&
                 statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
         if (shouldUnlockOnMenuPressed) {
-            shadeController.animateCollapseShadeForced()
-            return true
+            return collapseShadeLockedOrShowPrimaryBouncer()
         }
         return false
     }
 
-    private fun dispatchSpaceEvent(): Boolean {
-        if (isDeviceAwake() && statusBarStateController.state != StatusBarState.SHADE) {
-            shadeController.animateCollapseShadeForced()
-            return true
+    private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
+        when (statusBarStateController.state) {
+            StatusBarState.SHADE -> return false
+            StatusBarState.SHADE_LOCKED -> {
+                shadeController.animateCollapseShadeForced()
+                return true
+            }
+            StatusBarState.KEYGUARD -> {
+                if (!statusBarKeyguardViewManager.primaryBouncerIsShowing()) {
+                    statusBarKeyguardViewManager.showPrimaryBouncer(true)
+                    return true
+                }
+            }
         }
         return false
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index bbe6823..900413c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -46,6 +47,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
+@ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardKeyEventInteractorTest : SysuiTestCase() {
@@ -132,58 +134,73 @@
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_awakeKeyguard_showsPrimaryBouncer() {
         powerInteractor.setAwakeForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_awakeShadeLocked_collapsesShade() {
         powerInteractor.setAwakeForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
-
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
+    fun dispatchKeyEvent_menuActionUp_asleepKeyguard_neverCollapsesShade() {
         powerInteractor.setAsleepForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
+        verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
     }
 
     @Test
-    fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
+    fun dispatchKeyEvent_spaceActionUp_awakeKeyguard_collapsesShade() {
         powerInteractor.setAwakeForTest()
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+        whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
 
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
+    }
 
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
+    @Test
+    fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_awakeKeyguard_showsPrimaryBouncer() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_awakeKeyguard_primaryBouncerAlreadyShowing() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(true)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+        verifyActionsDoNothing(KeyEvent.KEYCODE_ENTER)
+    }
+
+    @Test
+    fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
+        powerInteractor.setAwakeForTest()
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
     }
 
     @Test
@@ -253,4 +270,42 @@
             .isFalse()
         verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
     }
+
+    private fun verifyActionUpCollapsesTheShade(keycode: Int) {
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(shadeController).animateCollapseShadeForced()
+    }
+
+    private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
+    }
+
+    private fun verifyActionsDoNothing(keycode: Int) {
+        // action down: does nothing
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+        // action up: doesNothing
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+    }
 }
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7323b0f..977b276 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -220,6 +220,7 @@
     name: "aapt2-protos",
     tools: [":soong_zip"],
     srcs: [
+        "ApkInfo.proto",
         "Configuration.proto",
         "ResourcesInternal.proto",
         "ResourceMetadata.proto",