Merge "Reland "Dismiss freeform tasks occluding keyguard in folded transitions"" into udc-qpr-dev
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index f0fb2f9..64940cb 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -673,8 +673,8 @@
                     synchronized (mInterfaceLock) {
                         try {
                             if (mSessionProcessor != null) {
-                                mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
                                 mInitialized = true;
+                                mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
                             } else {
                                 Log.v(TAG, "Failed to start capture session, session " +
                                                 " released before extension start!");
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 31d6f16..c7226f6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5342,7 +5342,7 @@
          fingerprint and face. If a dual-modality device only enrolled a single biometric and
          experiences high FRR (above threshold), system notification will be sent to encourage user
          to enroll the other eligible biometric. -->
-    <fraction name="config_biometricNotificationFrrThreshold">30%</fraction>
+    <fraction name="config_biometricNotificationFrrThreshold">25%</fraction>
 
     <!-- The component name for the default profile supervisor, which can be set as a profile owner
     even after user setup is complete. The defined component should be used for supervision purposes
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 1e6e503..0112e32 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -518,8 +518,11 @@
             return WindowAreaComponent.STATUS_UNSUPPORTED;
         }
 
-        if (mCurrentDeviceState == mConcurrentDisplayState
-                || !ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState)
+        if (mCurrentDeviceState == mConcurrentDisplayState) {
+            return WindowAreaComponent.STATUS_ACTIVE;
+        }
+
+        if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState)
                 || isDeviceFolded()) {
             return WindowAreaComponent.STATUS_UNAVAILABLE;
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f60f8db..765edd7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1948,6 +1948,9 @@
             cacheName = Settings.System.ALARM_ALERT_CACHE;
         }
         if (cacheName != null) {
+            if (!isValidAudioUri(name, value)) {
+                return false;
+            }
             final File cacheFile = new File(
                     getRingtoneCacheDir(owningUserId), cacheName);
             cacheFile.delete();
@@ -1980,6 +1983,34 @@
         }
     }
 
+    private boolean isValidAudioUri(String name, String uri) {
+        if (uri != null) {
+            Uri audioUri = Uri.parse(uri);
+            if (Settings.AUTHORITY.equals(
+                    ContentProvider.getAuthorityWithoutUserId(audioUri.getAuthority()))) {
+                // Don't accept setting the default uri to self-referential URIs like
+                // Settings.System.DEFAULT_RINGTONE_URI, which is an alias to the value of this
+                // setting.
+                return false;
+            }
+            final String mimeType = getContext().getContentResolver().getType(audioUri);
+            if (mimeType == null) {
+                Slog.e(LOG_TAG,
+                        "mutateSystemSetting for setting: " + name + " URI: " + audioUri
+                        + " ignored: failure to find mimeType (no access from this context?)");
+                return false;
+            }
+            if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg")
+                    || mimeType.equals("application/x-flac"))) {
+                Slog.e(LOG_TAG,
+                        "mutateSystemSetting for setting: " + name + " URI: " + audioUri
+                        + " ignored: associated mimeType: " + mimeType + " is not an audio type");
+                return false;
+            }
+        }
+        return true;
+    }
+
     private boolean hasWriteSecureSettingsPermission() {
         // Write secure settings is a more protected permission. If caller has it we are good.
         return getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index c1b905a..91547a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -144,13 +144,24 @@
         }
     }
 
-    private fun removeHun(animate: Boolean) {
-        if (!headsUpManager.isAlerting(notificationKey)) {
-            return
-        }
+    private val headsUpNotificationRow: ExpandableNotificationRow? get() {
+        val summaryEntry = notificationEntry.parent?.summary
 
+        return when {
+            headsUpManager.isAlerting(notificationKey) -> notification
+            summaryEntry == null -> null
+            headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row
+            else -> null
+        }
+    }
+
+    private fun removeHun(animate: Boolean) {
+        val row = headsUpNotificationRow ?: return
+
+        // TODO: b/297247841 - Call on the row we're removing, which may differ from notification.
         HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(notification, animate)
-        headsUpManager.removeNotification(notificationKey, true /* releaseImmediately */, animate)
+
+        headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate)
     }
 
     override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index e66eb70..470d340 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar.notification
 
+import android.app.Notification.GROUP_ALERT_SUMMARY
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
@@ -7,20 +8,26 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.policy.HeadsUpUtil
+import com.android.systemui.tests.R
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import kotlinx.coroutines.test.TestScope
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertSame
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
@@ -118,6 +125,35 @@
     }
 
     @Test
+    fun testAlertingSummaryHunRemovedOnNonAlertingChildLaunch() {
+        val GROUP_KEY = "test_group_key"
+
+        val summary = NotificationEntryBuilder().setGroup(mContext, GROUP_KEY).setId(0).apply {
+            modifyNotification(mContext).setSmallIcon(R.drawable.ic_person)
+        }.build()
+        assertNotSame(summary.key, notification.entry.key)
+
+        notificationTestHelper.createRow(summary)
+
+        GroupEntryBuilder().setKey(GROUP_KEY).setSummary(summary).addChild(notification.entry)
+                .build()
+        assertSame(summary, notification.entry.parent?.summary)
+
+        `when`(headsUpManager.isAlerting(notificationKey)).thenReturn(false)
+        `when`(headsUpManager.isAlerting(summary.key)).thenReturn(true)
+
+        assertNotSame(GROUP_ALERT_SUMMARY, summary.sbn.notification.groupAlertBehavior)
+        assertNotSame(GROUP_ALERT_SUMMARY, notification.entry.sbn.notification.groupAlertBehavior)
+
+        controller.onLaunchAnimationEnd(isExpandingFullyAbove = true)
+
+        verify(headsUpManager).removeNotification(
+                summary.key, true /* releaseImmediately */, false /* animate */)
+        verify(headsUpManager, never()).removeNotification(
+                notification.entry.key, true /* releaseImmediately */, false /* animate */)
+    }
+
+    @Test
     fun testNotificationIsExpandingDuringAnimation() {
         controller.onIntentStarted(willAnimate = true)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1ab2b38..b7574a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -269,6 +269,10 @@
         return generateRow(notification, PKG, UID, USER_HANDLE, mDefaultInflationFlags);
     }
 
+    public ExpandableNotificationRow createRow(NotificationEntry entry) throws Exception {
+        return generateRow(entry, mDefaultInflationFlags);
+    }
+
     /**
      * Create a row with the specified content views inflated in addition to the default.
      *
@@ -538,18 +542,6 @@
             @InflationFlag int extraInflationFlags,
             int importance)
             throws Exception {
-        // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
-        //  set, but we do not want to override an existing value that is needed by a specific test.
-        mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
-
-        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                mContext.LAYOUT_INFLATER_SERVICE);
-        mRow = (ExpandableNotificationRow) inflater.inflate(
-                R.layout.status_bar_notification_row,
-                null /* root */,
-                false /* attachToRoot */);
-        ExpandableNotificationRow row = mRow;
-
         final NotificationChannel channel =
                 new NotificationChannel(
                         notification.getChannelId(),
@@ -569,6 +561,25 @@
                 .setChannel(channel)
                 .build();
 
+        return generateRow(entry, extraInflationFlags);
+    }
+
+    private ExpandableNotificationRow generateRow(
+            NotificationEntry entry,
+            @InflationFlag int extraInflationFlags)
+            throws Exception {
+        // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+        //  set, but we do not want to override an existing value that is needed by a specific test.
+        mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
+        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                mContext.LAYOUT_INFLATER_SERVICE);
+        mRow = (ExpandableNotificationRow) inflater.inflate(
+                R.layout.status_bar_notification_row,
+                null /* root */,
+                false /* attachToRoot */);
+        ExpandableNotificationRow row = mRow;
+
         entry.setRow(row);
         mIconManager.createIcons(entry);
 
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 496bdf4..0eb459f 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -541,7 +541,6 @@
         }
         loglogi("addCompatibleAudioDevice: dev=" + ada);
         final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        initSAState(deviceState);
         AdiDeviceState updatedDevice = null; // non-null on update.
         if (deviceState != null) {
             if (forceEnable && !deviceState.isSAEnabled()) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 54b34de..3d1b162 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -45,12 +45,12 @@
     private static final String TAG = "AuthenticationStatsCollector";
 
     // The minimum number of attempts that will calculate the FRR and trigger the notification.
-    private static final int MINIMUM_ATTEMPTS = 500;
+    private static final int MINIMUM_ATTEMPTS = 150;
     // Upload the data every 50 attempts (average number of daily authentications).
     private static final int AUTHENTICATION_UPLOAD_INTERVAL = 50;
     // The maximum number of eligible biometric enrollment notification can be sent.
     @VisibleForTesting
-    static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
+    static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 1;
 
     @NonNull private final Context mContext;