Merge "Update the DM sysprop flags to override gantry's" into main
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index ea0f049..3e12a1a 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -13,3 +13,10 @@
     description: "Bind wallpaper service on its own thread instead of system_server's main handler during a user switch."
     bug: "302100344"
 }
+
+flag {
+    name: "support_communal_profile"
+    namespace: "multiuser"
+    description: "Framework support for communal profile."
+    bug: "285426179"
+}
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index bbfed24..883f157 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -82,8 +82,8 @@
         DVORAK(4, LAYOUT_TYPE_DVORAK),
         COLEMAK(5, LAYOUT_TYPE_COLEMAK),
         WORKMAN(6, LAYOUT_TYPE_WORKMAN),
-        TURKISH_F(7, LAYOUT_TYPE_TURKISH_F),
-        TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q),
+        TURKISH_Q(7, LAYOUT_TYPE_TURKISH_Q),
+        TURKISH_F(8, LAYOUT_TYPE_TURKISH_F),
         EXTENDED(9, LAYOUT_TYPE_EXTENDED);
 
         private final int mValue;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4360c5a..7ef81ab 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1151,6 +1151,14 @@
     <!-- Allows activities to be launched on a long press on power during device setup. -->
     <bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool>
 
+    <!-- Control the behavior when the user short presses the settings button.
+            0 - Nothing
+            1 - Launch notification panel
+         This needs to match the constants in
+         com/android/server/policy/PhoneWindowManager.java
+    -->
+    <integer name="config_shortPressOnSettingsBehavior">0</integer>
+
     <!-- Control the behavior when the user short presses the power button.
             0 - Nothing
             1 - Go to sleep (doze)
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7f30695..643f4b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1816,6 +1816,7 @@
   <java-symbol type="integer" name="config_lidNavigationAccessibility" />
   <java-symbol type="integer" name="config_lidOpenRotation" />
   <java-symbol type="integer" name="config_longPressOnHomeBehavior" />
+  <java-symbol type="integer" name="config_shortPressOnSettingsBehavior" />
   <java-symbol type="layout" name="global_actions" />
   <java-symbol type="layout" name="global_actions_item" />
   <java-symbol type="layout" name="global_actions_silent_mode" />
diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
index 6744922..fc53cba 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
@@ -12,9 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-
-type FULL
+type OVERLAY
 
 ### Basic QWERTY keys ###
 
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 88bb30b..7f23f74 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -227,13 +227,6 @@
         android:label="@string/keyboard_layout_turkish"
         android:keyboardLayout="@raw/keyboard_layout_turkish"
         android:keyboardLocale="tr-Latn"
-        android:keyboardLayoutType="qwerty" />
-
-    <keyboard-layout
-        android:name="keyboard_layout_turkish"
-        android:label="@string/keyboard_layout_turkish"
-        android:keyboardLayout="@raw/keyboard_layout_turkish"
-        android:keyboardLocale="tr-Latn"
         android:keyboardLayoutType="turkish_q" />
 
     <keyboard-layout
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 39173d9..58c7bdb 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -130,10 +130,23 @@
 sealed interface UserAction
 
 /** The user navigated back, either using a gesture or by triggering a KEYCODE_BACK event. */
-object Back : UserAction
+data object Back : UserAction
 
 /** The user swiped on the container. */
-enum class Swipe : UserAction {
+data class Swipe(
+    val direction: SwipeDirection,
+    val pointerCount: Int = 1,
+    val fromEdge: Edge? = null,
+) : UserAction {
+    companion object {
+        val Left = Swipe(SwipeDirection.Left)
+        val Up = Swipe(SwipeDirection.Up)
+        val Right = Swipe(SwipeDirection.Right)
+        val Down = Swipe(SwipeDirection.Down)
+    }
+}
+
+enum class SwipeDirection {
     Up,
     Down,
     Left,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index c3a3752..ee310ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -36,13 +36,14 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.isVisible
 import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.qualifiers.KeyguardRootView
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
@@ -99,7 +100,9 @@
         return buildMap {
             up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) }
             left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) }
-            this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade)
+            this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] =
+                SceneModel(SceneKey.QuickSettings)
+            this[UserAction.Swipe(direction = Direction.DOWN)] = SceneModel(SceneKey.Shade)
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 2ee461f..f35ea83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -22,6 +22,7 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
@@ -41,7 +42,12 @@
     override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
-                    UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
+                    UserAction.Swipe(
+                        pointerCount = 2,
+                        fromEdge = Edge.TOP,
+                        direction = Direction.DOWN,
+                    ) to SceneModel(SceneKey.QuickSettings),
+                    UserAction.Swipe(direction = Direction.DOWN) to SceneModel(SceneKey.Shade),
                 )
             )
             .asStateFlow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 6359ce60..0da562b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -35,15 +35,18 @@
 import androidx.compose.ui.input.pointer.motionEventSpy
 import androidx.compose.ui.input.pointer.pointerInput
 import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Edge as SceneTransitionEdge
 import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
 import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
@@ -181,12 +184,24 @@
 private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction {
     return when (this) {
         is UserAction.Swipe ->
-            when (this.direction) {
-                Direction.LEFT -> Swipe.Left
-                Direction.UP -> Swipe.Up
-                Direction.RIGHT -> Swipe.Right
-                Direction.DOWN -> Swipe.Down
-            }
+            Swipe(
+                pointerCount = pointerCount,
+                fromEdge =
+                    when (this.fromEdge) {
+                        null -> null
+                        Edge.LEFT -> SceneTransitionEdge.Left
+                        Edge.TOP -> SceneTransitionEdge.Top
+                        Edge.RIGHT -> SceneTransitionEdge.Right
+                        Edge.BOTTOM -> SceneTransitionEdge.Bottom
+                    },
+                direction =
+                    when (this.direction) {
+                        Direction.LEFT -> SwipeDirection.Left
+                        Direction.UP -> SwipeDirection.Up
+                        Direction.RIGHT -> SwipeDirection.Right
+                        Direction.DOWN -> SwipeDirection.Down
+                    }
+            )
         is UserAction.Back -> Back
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 2b56d0c..d08d040 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -239,6 +239,8 @@
     protected void onDestroy() {
         super.onDestroy();
         if (mDialog != null) {
+            mDialog.setOnDismissListener(null);
+            mDialog.setOnCancelListener(null);
             mDialog.dismiss();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 4bc93a8..2e45353 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -61,12 +61,17 @@
     data class Swipe(
         /** The direction of the swipe. */
         val direction: Direction,
+        /**
+         * The edge from which the swipe originated or `null`, if the swipe didn't start close to an
+         * edge.
+         */
+        val fromEdge: Edge? = null,
         /** The number of pointers that were used (for example, one or two fingers). */
         val pointerCount: Int = 1,
     ) : UserAction
 
     /** The user has hit the back button or performed the back navigation gesture. */
-    object Back : UserAction
+    data object Back : UserAction
 }
 
 /** Enumerates all known "cardinal" directions for user actions. */
@@ -76,3 +81,11 @@
     RIGHT,
     DOWN,
 }
+
+/** Enumerates all known edges from which a swipe can start. */
+enum class Edge {
+    LEFT,
+    TOP,
+    RIGHT,
+    BOTTOM,
+}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 652e6cf..39f0b13 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -105,16 +105,21 @@
     public void resetHdrConfig(HdrBrightnessData data, int width, int height,
             float minimumHdrPercentOfScreen, IBinder displayToken) {
         mHdrBrightnessData = data;
-        mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
+        mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1
+                : (float) (width * height) * minimumHdrPercentOfScreen;
         if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
             if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
                 mHdrListener.unregister(mRegisteredDisplayToken);
                 mHdrVisible = false;
+                mRegisteredDisplayToken = null;
             }
-            if (displayToken != null) { // new token not null, subscribe
+            // new token not null and hdr min % of the screen is set, subscribe.
+            // e.g. for virtual display, HBM data will be missing and HdrListener
+            // should not be registered
+            if (displayToken != null && mHdrListener.mHdrMinPixels > 0) {
                 mHdrListener.register(displayToken);
+                mRegisteredDisplayToken = displayToken;
             }
-            mRegisteredDisplayToken = displayToken;
         }
         recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
     }
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 0eb620f..bad6bf0 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -71,6 +71,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -99,7 +100,7 @@
  *
  * @hide
  */
-final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+class KeyboardLayoutManager implements InputManager.InputDeviceListener {
 
     private static final String TAG = "KeyboardLayoutManager";
 
@@ -1295,7 +1296,8 @@
     }
 
     @SuppressLint("MissingPermission")
-    private List<ImeInfo> getImeInfoListForLayoutMapping() {
+    @VisibleForTesting
+    public List<ImeInfo> getImeInfoListForLayoutMapping() {
         List<ImeInfo> imeInfoList = new ArrayList<>();
         UserManager userManager = Objects.requireNonNull(
                 mContext.getSystemService(UserManager.class));
@@ -1402,7 +1404,8 @@
         }
     }
 
-    private static class ImeInfo {
+    @VisibleForTesting
+    public static class ImeInfo {
         @UserIdInt int mUserId;
         @NonNull InputMethodSubtypeHandle mImeSubtypeHandle;
         @Nullable InputMethodSubtype mImeSubtype;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 097656c..3a6664a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -333,6 +333,11 @@
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
 
+    // must match: config_shortPressOnSettingsBehavior in config.xml
+    static final int SHORT_PRESS_SETTINGS_NOTHING = 0;
+    static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1;
+    static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
+
     static final int PENDING_KEY_NULL = -1;
 
     // Must match: config_shortPressOnStemPrimaryBehavior in config.xml
@@ -611,6 +616,9 @@
     // What we do when the user double-taps on home
     int mDoubleTapOnHomeBehavior;
 
+    // What we do when the user presses on settings
+    int mShortPressOnSettingsBehavior;
+
     // Must match config_primaryShortPressTargetActivity in config.xml
     ComponentName mPrimaryShortPressTargetActivity;
 
@@ -2766,6 +2774,13 @@
         if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
         }
+
+        mShortPressOnSettingsBehavior = res.getInteger(
+                com.android.internal.R.integer.config_shortPressOnSettingsBehavior);
+        if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING
+                || mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) {
+            mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING;
+        }
     }
 
     private void updateSettings() {
@@ -3632,6 +3647,15 @@
                 Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
                         + " interceptKeyBeforeQueueing");
                 return true;
+            case KeyEvent.KEYCODE_SETTINGS:
+                if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) {
+                    if (!down) {
+                        toggleNotificationPanel();
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
+                    }
+                    return true;
+                }
+                break;
         }
         if (isValidGlobalKey(keyCode)
                 && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
@@ -6272,6 +6296,9 @@
                 pw.print("mLongPressOnPowerBehavior=");
                 pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
         pw.print(prefix);
+        pw.print("mShortPressOnSettingsBehavior=");
+        pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior));
+        pw.print(prefix);
         pw.print("mLongPressOnPowerAssistantTimeoutMs=");
         pw.println(mLongPressOnPowerAssistantTimeoutMs);
         pw.print(prefix);
@@ -6470,6 +6497,17 @@
         }
     }
 
+    private static String shortPressOnSettingsBehaviorToString(int behavior) {
+        switch (behavior) {
+            case SHORT_PRESS_SETTINGS_NOTHING:
+                return "SHORT_PRESS_SETTINGS_NOTHING";
+            case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL:
+                return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
     private static String veryLongPressOnPowerBehaviorToString(int behavior) {
         switch (behavior) {
             case VERY_LONG_PRESS_POWER_NOTHING:
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index c63fac9..ee187ba 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -22,6 +22,9 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -97,6 +100,37 @@
     }
 
     @Test
+    public void testRegisterHdrListener() {
+        verify(mMockHdrInfoListener).register(mMockBinder);
+    }
+
+    @Test
+    public void testRegisterOtherHdrListenerWhenCalledWithOtherToken() {
+        IBinder otherBinder = mock(IBinder.class);
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, otherBinder);
+
+        verify(mMockHdrInfoListener).unregister(mMockBinder);
+        verify(mMockHdrInfoListener).register(otherBinder);
+    }
+
+    @Test
+    public void testRegisterHdrListenerOnceWhenCalledWithSameToken() {
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
+
+        verify(mMockHdrInfoListener, never()).unregister(mMockBinder);
+        verify(mMockHdrInfoListener, times(1)).register(mMockBinder);
+    }
+
+    @Test
+    public void testRegisterNotCalledIfHbmConfigIsMissing() {
+        IBinder otherBinder = mock(IBinder.class);
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder);
+
+        verify(mMockHdrInfoListener).unregister(mMockBinder);
+        verify(mMockHdrInfoListener, never()).register(otherBinder);
+    }
+
+    @Test
     public void testClamper_AmbientLuxChangesAboveLimit() {
         mHdrClamper.onAmbientLuxChange(500);
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 0a7bb00..71098aa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -20,6 +20,7 @@
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
 
 import android.platform.test.annotations.Presubmit;
 import android.view.KeyEvent;
@@ -284,6 +285,16 @@
                         KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_H, META_ON}};
     }
 
+    @Keep
+    private static Object[][] shortPressOnSettingsTestArguments() {
+        // testName, testKeys, shortPressOnSettingsBehavior, expectedLogEvent, expectedKey,
+        // expectedModifierState
+        return new Object[][]{
+                {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
+                        SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL,
+                        KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_SETTINGS, 0}};
+    }
+
     @Before
     public void setUp() {
         setUpPhoneWindowManager(/*supportSettingsUpdate*/ true);
@@ -294,6 +305,7 @@
         mPhoneWindowManager.overrideEnableBugReportTrigger(true);
         mPhoneWindowManager.overrideStatusBarManagerInternal();
         mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.overrideSendBroadcast();
         mPhoneWindowManager.overrideUserSetupComplete();
         mPhoneWindowManager.setupAssistForLaunch();
         mPhoneWindowManager.overrideTogglePanel();
@@ -330,4 +342,15 @@
         mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
                 expectedKey, expectedModifierState, "Failed while executing " + testName);
     }
+
+    @Test
+    @Parameters(method = "shortPressOnSettingsTestArguments")
+    public void testShortPressOnSettings(String testName, int[] testKeys,
+            int shortPressOnSettingsBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey,
+            int expectedModifierState) {
+        mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior);
+        sendKeyCombination(testKeys, 0 /* duration */);
+        mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
+                expectedKey, expectedModifierState, "Failed while executing " + testName);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ef28ffa..2244dbe 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -375,6 +375,10 @@
         mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior;
     }
 
+    void overrideShortPressOnSettingsBehavior(int behavior) {
+        mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior;
+    }
+
     void overrideCanStartDreaming(boolean canDream) {
         doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean());
     }
@@ -484,6 +488,10 @@
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
     }
 
+    void overrideSendBroadcast() {
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+    }
+
     void overrideUserSetupComplete() {
         doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
     }
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index f910b8b..cf2d5d6 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,6 +9,10 @@
 
 android_test {
     name: "InputTests",
+    defaults: [
+        // For ExtendedMockito dependencies.
+        "modules-utils-testable-device-config-defaults",
+    ],
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index b6477510..fa86e9c 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -32,11 +32,16 @@
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.provider.Settings
+import android.util.proto.ProtoOutputStream
 import android.view.InputDevice
 import android.view.inputmethod.InputMethodInfo
 import android.view.inputmethod.InputMethodSubtype
 import androidx.test.core.R
 import androidx.test.core.app.ApplicationProvider
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.os.KeyboardConfiguredProto
+import com.android.internal.util.FrameworkStatsLog
+import com.android.modules.utils.testing.ExtendedMockitoRule
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
@@ -46,9 +51,9 @@
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.junit.MockitoJUnit
 import java.io.FileNotFoundException
 import java.io.FileOutputStream
 import java.io.IOException
@@ -96,6 +101,9 @@
         private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us"
         private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk"
         private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1"
+        const val LAYOUT_TYPE_QWERTZ = 2
+        const val LAYOUT_TYPE_QWERTY = 1
+        const val LAYOUT_TYPE_DEFAULT = 0
     }
 
     private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME)
@@ -103,8 +111,10 @@
     private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR =
         createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME)
 
-    @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    @JvmField
+    @Rule
+    val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+            .mockStatic(FrameworkStatsLog::class.java).build()!!
 
     @Mock
     private lateinit var iInputManager: IInputManager
@@ -145,7 +155,9 @@
             override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
         })
         testLooper = TestLooper()
-        keyboardLayoutManager = KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+        keyboardLayoutManager = Mockito.spy(
+            KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+        )
         setupInputDevices()
         setupBroadcastReceiver()
         setupIme()
@@ -827,6 +839,100 @@
         }
     }
 
+    @Test
+    fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() {
+        val imeInfos = listOf(
+                KeyboardLayoutManager.ImeInfo(0, imeInfo,
+                        createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+            ExtendedMockito.verify {
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.eq(keyboardDevice.vendorId),
+                        ArgumentMatchers.eq(keyboardDevice.productId),
+                        ArgumentMatchers.eq(createByteArray(
+                                KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+                                LAYOUT_TYPE_DEFAULT,
+                                "German",
+                                KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+                                "de-Latn",
+                                LAYOUT_TYPE_QWERTZ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() {
+        val imeInfos = listOf(
+                KeyboardLayoutManager.ImeInfo(0, imeInfo,
+                        createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
+            ExtendedMockito.verify {
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
+                        ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
+                        ArgumentMatchers.eq(createByteArray(
+                                "en",
+                                LAYOUT_TYPE_QWERTY,
+                                "English (US)",
+                                KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+                                "de-Latn",
+                                LAYOUT_TYPE_QWERTZ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() {
+        val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+            ExtendedMockito.verify {
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.eq(keyboardDevice.vendorId),
+                        ArgumentMatchers.eq(keyboardDevice.productId),
+                        ArgumentMatchers.eq(createByteArray(
+                                KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+                                LAYOUT_TYPE_DEFAULT,
+                                "Default",
+                                KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+                                KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+                                LAYOUT_TYPE_DEFAULT))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testConfigurationNotLogged_onInputDeviceChanged() {
+        val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+        Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+        NewSettingsApiFlag(true).use {
+            keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+            ExtendedMockito.verify({
+                FrameworkStatsLog.write(
+                        ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+                        ArgumentMatchers.anyBoolean(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any(ByteArray::class.java)
+                )
+            }, Mockito.times(0))
+        }
+    }
+
     private fun assertCorrectLayout(
         device: InputDevice,
         imeSubtype: InputMethodSubtype,
@@ -842,18 +948,60 @@
     }
 
     private fun createImeSubtype(): InputMethodSubtype =
-        InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++).build()
+            createImeSubtypeForLanguageTagAndLayoutType(null, null)
 
     private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype =
-        InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++)
-            .setLanguageTag(languageTag).build()
+            createImeSubtypeForLanguageTagAndLayoutType(languageTag, null)
 
     private fun createImeSubtypeForLanguageTagAndLayoutType(
-        languageTag: String,
-        layoutType: String
-    ): InputMethodSubtype =
-        InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++)
-            .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build()
+            languageTag: String?,
+            layoutType: String?
+    ): InputMethodSubtype {
+        val builder = InputMethodSubtype.InputMethodSubtypeBuilder()
+                .setSubtypeId(nextImeSubtypeId++)
+                .setIsAuxiliary(false)
+                .setSubtypeMode("keyboard")
+        if (languageTag != null && layoutType != null) {
+            builder.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType)
+        } else if (languageTag != null) {
+            builder.setLanguageTag(languageTag)
+        }
+        return builder.build()
+    }
+
+    private fun createByteArray(
+            expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String,
+            expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray {
+        val proto = ProtoOutputStream()
+        val keyboardLayoutConfigToken = proto.start(
+                KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
+                expectedLanguageTag
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
+                expectedLayoutType
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
+                expectedLayoutName
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
+                expectedCriteria
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG,
+                expectedImeLanguageTag
+        )
+        proto.write(
+                KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
+                expectedImeLayoutType
+        )
+        proto.end(keyboardLayoutConfigToken);
+        return proto.bytes
+    }
 
     private fun hasLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean {
         for (kl in layoutList) {