summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/InsetsController.java2
-rw-r--r--core/java/com/android/internal/os/anr/AnrLatencyTracker.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt3
-rw-r--r--packages/SettingsLib/res/drawable/ic_docked_tablet.xml26
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item_advanced.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt128
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java72
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java13
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java16
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java26
-rw-r--r--services/core/java/com/android/server/wm/DeviceStateController.java32
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java4
-rw-r--r--services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java5
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java30
48 files changed, 807 insertions, 179 deletions
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index f570c6d15672..7e4e4022f00f 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1374,7 +1374,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// The requested visibilities should be delayed as well. Otherwise, we might override
// the insets visibility before playing animation.
- setRequestedVisibleTypes(mReportedRequestedVisibleTypes, typesReady);
+ setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
if (!fromIme) {
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
index 3ba4ea55b5d3..80d753c7ed09 100644
--- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -120,6 +120,10 @@ public class AnrLatencyTracker implements AutoCloseable {
private long mPreDumpIfLockTooSlowStartUptime;
private long mPreDumpIfLockTooSlowDuration = 0;
+ private long mNotifyAppUnresponsiveStartUptime;
+ private long mNotifyAppUnresponsiveDuration = 0;
+ private long mNotifyWindowUnresponsiveStartUptime;
+ private long mNotifyWindowUnresponsiveDuration = 0;
private final int mAnrRecordPlacedOnQueueCookie =
sNextAnrRecordPlacedOnQueueCookieGenerator.incrementAndGet();
@@ -425,11 +429,36 @@ public class AnrLatencyTracker implements AutoCloseable {
anrSkipped("dumpStackTraces");
}
+ /** Records the start of AnrController#notifyAppUnresponsive. */
+ public void notifyAppUnresponsiveStarted() {
+ mNotifyAppUnresponsiveStartUptime = getUptimeMillis();
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyAppUnresponsive()");
+ }
+
+ /** Records the end of AnrController#notifyAppUnresponsive. */
+ public void notifyAppUnresponsiveEnded() {
+ mNotifyAppUnresponsiveDuration = getUptimeMillis() - mNotifyAppUnresponsiveStartUptime;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of AnrController#notifyWindowUnresponsive. */
+ public void notifyWindowUnresponsiveStarted() {
+ mNotifyWindowUnresponsiveStartUptime = getUptimeMillis();
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyWindowUnresponsive()");
+ }
+
+ /** Records the end of AnrController#notifyWindowUnresponsive. */
+ public void notifyWindowUnresponsiveEnded() {
+ mNotifyWindowUnresponsiveDuration = getUptimeMillis()
+ - mNotifyWindowUnresponsiveStartUptime;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
/**
* Returns latency data as a comma separated value string for inclusion in ANR report.
*/
public String dumpAsCommaSeparatedArrayWithHeader() {
- return "DurationsV4: " + mAnrTriggerUptime
+ return "DurationsV5: " + mAnrTriggerUptime
/* triggering_to_app_not_responding_duration = */
+ "," + (mAppNotRespondingStartUptime - mAnrTriggerUptime)
/* app_not_responding_duration = */
@@ -480,6 +509,10 @@ public class AnrLatencyTracker implements AutoCloseable {
+ "," + (mCopyingFirstPidSucceeded ? 1 : 0)
/* preDumpIfLockTooSlow_duration = */
+ "," + mPreDumpIfLockTooSlowDuration
+ /* notifyAppUnresponsive_duration = */
+ + "," + mNotifyAppUnresponsiveDuration
+ /* notifyWindowUnresponsive_duration = */
+ + "," + mNotifyWindowUnresponsiveDuration
+ "\n\n";
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index c25352b6e57d..513638eeb960 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -185,6 +185,9 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
@Override
public void onTransitionFinished(
WindowContainerTransaction wct, SurfaceControl.Transaction sct) {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mMainExecutor.execute(() -> {
mStartedTransitions.remove(transition);
finishCallback.onTransitionFinished(wct, null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index dc0a2943d394..f9d615ad0cf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1079,10 +1079,28 @@ public class PipTransition extends PipTransitionController {
}
final float alphaStart = show ? 0 : 1;
final float alphaEnd = show ? 1 : 0;
+ final PipAnimationController.PipTransactionHandler transactionHandler =
+ new PipAnimationController.PipTransactionHandler() {
+ @Override
+ public boolean handlePipTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, Rect destinationBounds, float alpha) {
+ if (alpha == 0) {
+ if (show) {
+ tx.setPosition(leash, destinationBounds.left, destinationBounds.top);
+ } else {
+ // Put PiP out of the display so it won't block touch when it is hidden.
+ final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
+ final int max = Math.max(displayBounds.width(), displayBounds.height());
+ tx.setPosition(leash, max, max);
+ }
+ }
+ return false;
+ }
+ };
mPipAnimationController
.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
.setTransitionDirection(TRANSITION_DIRECTION_SAME)
- .setPipAnimationCallback(mPipAnimationCallback)
+ .setPipTransactionHandler(transactionHandler)
.setDuration(mEnterExitAnimationDuration)
.start();
mHasFadeOut = !show;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index e0dbfa3853cc..676c150815ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.Postsubmit
import android.tools.common.NavBar
-import android.tools.common.Rotation
import android.tools.common.flicker.subject.region.RegionSubject
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -105,8 +104,6 @@ class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) :
@JvmStatic
fun getParams(): List<FlickerTest> {
return FlickerTestFactory.nonRotationTests(
- // TODO(b/283963801) address entireScreenCovered test faliure in landscape.
- supportedRotations = listOf(Rotation.ROTATION_0),
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
diff --git a/packages/SettingsLib/res/drawable/ic_docked_tablet.xml b/packages/SettingsLib/res/drawable/ic_docked_tablet.xml
new file mode 100644
index 000000000000..96a4900f361f
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_docked_tablet.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M480,280Q497,280 508.5,268.5Q520,257 520,240Q520,223 508.5,211.5Q497,200 480,200Q463,200 451.5,211.5Q440,223 440,240Q440,257 451.5,268.5Q463,280 480,280ZM120,720Q87,720 63.5,696.5Q40,673 40,640L60,160Q60,127 83.5,103.5Q107,80 140,80L820,80Q853,80 876.5,103.5Q900,127 900,160L920,640Q920,673 896.5,696.5Q873,720 840,720L120,720ZM120,640L840,640Q840,640 840,640Q840,640 840,640L820,160Q820,160 820,160Q820,160 820,160L140,160Q140,160 140,160Q140,160 140,160L120,640Q120,640 120,640Q120,640 120,640ZM320,880Q259,880 209.5,850Q160,820 160,765L160,720L240,720L240,760Q253,780 274.5,790Q296,800 320,800L640,800Q664,800 685.5,790.5Q707,781 720,761L720,720L800,720L800,765Q800,820 750.5,850Q701,880 640,880L320,880ZM480,400Q480,400 480,400Q480,400 480,400L480,400Q480,400 480,400Q480,400 480,400L480,400Q480,400 480,400Q480,400 480,400L480,400Q480,400 480,400Q480,400 480,400L480,400Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index 054193a5b323..a595566ef817 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -65,6 +65,8 @@
<TextView
android:id="@+id/volume_value"
android:animateLayoutChanges="true"
+ android:focusable="false"
+ android:importantForAccessibility="no"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -75,6 +77,8 @@
<TextView
android:id="@+id/title"
+ android:focusable="false"
+ android:importantForAccessibility="no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt
new file mode 100644
index 000000000000..495d3a13d961
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.systemui.unfold.util
+
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.Executor
+
+/**
+ * [UnfoldTransitionProgressProvider] that emits transition progress only when unfolding but not
+ * when folding, so we can play the animation only one way but not the other way.
+ */
+class UnfoldOnlyProgressProvider(
+ foldProvider: FoldProvider,
+ @Main private val executor: Executor,
+ private val sourceProvider: UnfoldTransitionProgressProvider,
+ private val scopedProvider: ScopedUnfoldTransitionProgressProvider =
+ ScopedUnfoldTransitionProgressProvider(sourceProvider)
+) : UnfoldTransitionProgressProvider by scopedProvider {
+
+ private var isFolded = false
+
+ init {
+ foldProvider.registerCallback(FoldListener(), executor)
+ sourceProvider.addCallback(SourceTransitionListener())
+ }
+
+ private inner class SourceTransitionListener : TransitionProgressListener {
+ override fun onTransitionFinished() {
+ // Disable scoped progress provider after the first unfold animation, so fold animation
+ // will not be propagated. It will be re-enabled after folding so we can play
+ // the unfold animation again.
+ if (!isFolded) {
+ scopedProvider.setReadyToHandleTransition(false)
+ }
+ }
+ }
+
+ private inner class FoldListener : FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ if (isFolded) {
+ scopedProvider.setReadyToHandleTransition(true)
+ }
+
+ this@UnfoldOnlyProgressProvider.isFolded = isFolded
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index fb160f2a2f00..69ce78ce30a8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -109,6 +109,7 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom
int ACTION_ERROR = 5;
int ACTION_USE_DEVICE_CREDENTIAL = 6;
int ACTION_START_DELAYED_FINGERPRINT_SENSOR = 7;
+ int ACTION_AUTHENTICATED_AND_CONFIRMED = 8;
/**
* When an action has occurred. The caller will only invoke this when the callback should
@@ -509,7 +510,8 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom
}
public void updateState(@BiometricState int newState) {
- Log.v(TAG, "newState: " + newState);
+ Log.d(TAG, "newState: " + newState);
+
mIconController.updateState(mState, newState);
switch (newState) {
@@ -533,8 +535,14 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom
}
announceForAccessibility(getResources()
.getString(R.string.biometric_dialog_authenticated));
- mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
- getDelayAfterAuthenticatedDurationMs());
+ if (mState == STATE_PENDING_CONFIRMATION) {
+ mHandler.postDelayed(() -> mCallback.onAction(
+ Callback.ACTION_AUTHENTICATED_AND_CONFIRMED),
+ getDelayAfterAuthenticatedDurationMs());
+ } else {
+ mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
+ getDelayAfterAuthenticatedDurationMs());
+ }
break;
case STATE_PENDING_CONFIRMATION:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 49ac26411d3e..43a3b9958ee5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -213,6 +213,9 @@ public class AuthContainerView extends LinearLayout
case AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR:
mConfig.mCallback.onStartFingerprintNow(getRequestId());
break;
+ case AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED:
+ animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
+ break;
default:
Log.e(TAG, "Unhandled action: " + action);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 765299d7401f..e5fa2090bf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -678,7 +678,7 @@ object Flags {
// TODO(b/283071711): Tracking bug
@JvmField
val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
- unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock")
+ releasedFlag(2401, "trim_resources_with_background_trim_on_lock")
// TODO:(b/283203305): Tracking bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index a8d22c48e709..5a8c2253b185 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -29,6 +29,7 @@ import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TransitionFlags;
import static android.view.WindowManager.TransitionOldType;
import static android.view.WindowManager.TransitionType;
@@ -49,6 +50,7 @@ import android.os.RemoteException;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.RotationUtils;
import android.util.Slog;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
@@ -73,6 +75,7 @@ import com.android.systemui.SystemUIApplication;
import com.android.systemui.settings.DisplayTracker;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.CounterRotator;
import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
@@ -103,7 +106,8 @@ public class KeyguardService extends Service {
}
private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers,
- SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+ SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
+ CounterRotator counterWallpaper) {
final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
for (int i = 0; i < info.getChanges().size(); i++) {
boolean changeIsWallpaper =
@@ -133,6 +137,25 @@ public class KeyguardService extends Service {
(change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0,
info, t, leashMap);
+ if (changeIsWallpaper) {
+ int rotateDelta = RotationUtils.deltaRotation(change.getStartRotation(),
+ change.getEndRotation());
+ if (rotateDelta != 0 && change.getParent() != null
+ && change.getMode() == TRANSIT_TO_BACK) {
+ final TransitionInfo.Change parent = info.getChange(change.getParent());
+ if (parent != null) {
+ float displayW = parent.getEndAbsBounds().width();
+ float displayH = parent.getEndAbsBounds().height();
+ counterWallpaper.setup(t, parent.getLeash(), rotateDelta, displayW,
+ displayH);
+ }
+ if (counterWallpaper.getSurface() != null) {
+ t.setLayer(counterWallpaper.getSurface(), -1);
+ counterWallpaper.addChild(t, leashMap.get(change.getLeash()));
+ }
+ }
+ }
+
out.add(target);
}
return out.toArray(new RemoteAnimationTarget[out.size()]);
@@ -163,6 +186,8 @@ public class KeyguardService extends Service {
return new IRemoteTransition.Stub() {
private final ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = new ArrayMap<>();
+ private final CounterRotator mCounterRotator = new CounterRotator();
+
@GuardedBy("mLeashMap")
private IRemoteTransitionFinishedCallback mFinishCallback = null;
@@ -175,9 +200,9 @@ public class KeyguardService extends Service {
synchronized (mLeashMap) {
final RemoteAnimationTarget[] apps =
- wrap(info, false /* wallpapers */, t, mLeashMap);
+ wrap(info, false /* wallpapers */, t, mLeashMap, mCounterRotator);
final RemoteAnimationTarget[] wallpapers =
- wrap(info, true /* wallpapers */, t, mLeashMap);
+ wrap(info, true /* wallpapers */, t, mLeashMap, mCounterRotator);
final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0];
// Set alpha back to 1 for the independent changes because we will be animating
@@ -231,11 +256,19 @@ public class KeyguardService extends Service {
@GuardedBy("mLeashMap")
private void finish() throws RemoteException {
+ SurfaceControl.Transaction finishTransaction = null;
+ if (mCounterRotator.getSurface() != null
+ && mCounterRotator.getSurface().isValid()) {
+ finishTransaction = new SurfaceControl.Transaction();
+ mCounterRotator.cleanUp(finishTransaction);
+ }
mLeashMap.clear();
final IRemoteTransitionFinishedCallback finishCallback = mFinishCallback;
if (finishCallback != null) {
mFinishCallback = null;
- finishCallback.onTransitionFinished(null /* wct */, null /* t */);
+ finishCallback.onTransitionFinished(null /* wct */, finishTransaction);
+ } else if (finishTransaction != null) {
+ finishTransaction.apply();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index c9f645dddd8d..af0abdff0c62 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -52,8 +52,7 @@ constructor(
override val key: String
get() = BuiltInKeyguardQuickAffordanceKeys.CAMERA
- override val pickerName: String
- get() = context.getString(R.string.accessibility_camera_button)
+ override fun pickerName(): String = context.getString(R.string.accessibility_camera_button)
override val pickerIconResourceId: Int
get() = R.drawable.ic_camera
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index ef0c9a175141..16385ec59aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -99,7 +99,7 @@ constructor(
override val key: String = BuiltInKeyguardQuickAffordanceKeys.DO_NOT_DISTURB
- override val pickerName: String = context.getString(R.string.quick_settings_dnd_label)
+ override fun pickerName(): String = context.getString(R.string.quick_settings_dnd_label)
override val pickerIconResourceId: Int = R.drawable.ic_do_not_disturb
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 3412f35669e3..ed8823a53fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -75,8 +75,7 @@ constructor(
override val key: String
get() = BuiltInKeyguardQuickAffordanceKeys.FLASHLIGHT
- override val pickerName: String
- get() = context.getString(R.string.quick_settings_flashlight_label)
+ override fun pickerName(): String = context.getString(R.string.quick_settings_flashlight_label)
override val pickerIconResourceId: Int
get() = R.drawable.ic_flashlight_off
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index a1e9137d1764..abb63c4d34ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -54,7 +54,7 @@ constructor(
override val key: String = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
- override val pickerName: String by lazy { context.getString(component.getTileTitleId()) }
+ override fun pickerName(): String = context.getString(component.getTileTitleId())
override val pickerIconResourceId: Int by lazy { component.getTileImageId() }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index e32edcb010e8..28dc5bdcc45f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -31,8 +31,6 @@ interface KeyguardQuickAffordanceConfig {
/** Unique identifier for this quick affordance. It must be globally unique. */
val key: String
- val pickerName: String
-
val pickerIconResourceId: Int
/**
@@ -43,6 +41,12 @@ interface KeyguardQuickAffordanceConfig {
val lockScreenState: Flow<LockScreenState>
/**
+ * Returns a user-visible [String] that should be shown as the name for the option in the
+ * wallpaper picker / settings app to select this quick affordance.
+ */
+ fun pickerName(): String
+
+ /**
* Returns the [PickerScreenState] representing the affordance in the settings or selector
* experience.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index da91572894f3..250356819baf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -34,6 +34,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -45,30 +46,32 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import javax.inject.Inject
@SysUISingleton
-class MuteQuickAffordanceConfig @Inject constructor(
- context: Context,
- private val userTracker: UserTracker,
- private val userFileManager: UserFileManager,
- private val ringerModeTracker: RingerModeTracker,
- private val audioManager: AudioManager,
- @Application private val coroutineScope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
+class MuteQuickAffordanceConfig
+@Inject
+constructor(
+ private val context: Context,
+ private val userTracker: UserTracker,
+ private val userFileManager: UserFileManager,
+ private val ringerModeTracker: RingerModeTracker,
+ private val audioManager: AudioManager,
+ @Application private val coroutineScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig {
private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE
override val key: String = BuiltInKeyguardQuickAffordanceKeys.MUTE
- override val pickerName: String = context.getString(R.string.volume_ringer_status_silent)
+ override fun pickerName(): String = context.getString(R.string.volume_ringer_status_silent)
override val pickerIconResourceId: Int = R.drawable.ic_notifications_silence
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
- ringerModeTracker.ringerModeInternal.asFlow()
+ ringerModeTracker.ringerModeInternal
+ .asFlow()
.onStart { getLastNonSilentRingerMode() }
.distinctUntilChanged()
.onEach { mode ->
@@ -78,17 +81,14 @@ class MuteQuickAffordanceConfig @Inject constructor(
}
}
.map { mode ->
- val (activationState, contentDescriptionRes) = when {
- audioManager.isVolumeFixed ->
- ActivationState.NotSupported to
- R.string.volume_ringer_hint_mute
- mode == AudioManager.RINGER_MODE_SILENT ->
- ActivationState.Active to
- R.string.volume_ringer_hint_mute
- else ->
- ActivationState.Inactive to
- R.string.volume_ringer_hint_unmute
- }
+ val (activationState, contentDescriptionRes) =
+ when {
+ audioManager.isVolumeFixed ->
+ ActivationState.NotSupported to R.string.volume_ringer_hint_mute
+ mode == AudioManager.RINGER_MODE_SILENT ->
+ ActivationState.Active to R.string.volume_ringer_hint_mute
+ else -> ActivationState.Inactive to R.string.volume_ringer_hint_unmute
+ }
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
@@ -130,32 +130,35 @@ class MuteQuickAffordanceConfig @Inject constructor(
}
/**
- * Gets the last non-silent ringer mode from shared-preferences if it exists. This is
- * cached by [MuteQuickAffordanceCoreStartable] while this affordance is selected
+ * Gets the last non-silent ringer mode from shared-preferences if it exists. This is cached by
+ * [MuteQuickAffordanceCoreStartable] while this affordance is selected
*/
private suspend fun getLastNonSilentRingerMode(): Int =
withContext(backgroundDispatcher) {
- userFileManager.getSharedPreferences(
+ userFileManager
+ .getSharedPreferences(
MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
Context.MODE_PRIVATE,
userTracker.userId
- ).getInt(
+ )
+ .getInt(
LAST_NON_SILENT_RINGER_MODE_KEY,
ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
- )
+ )
}
private fun <T> LiveData<T>.asFlow(): Flow<T?> =
conflatedCallbackFlow {
- val observer = Observer { value: T -> trySend(value) }
- observeForever(observer)
- send(value)
- awaitClose { removeObserver(observer) }
- }.flowOn(mainDispatcher)
+ val observer = Observer { value: T -> trySend(value) }
+ observeForever(observer)
+ send(value)
+ awaitClose { removeObserver(observer) }
+ }
+ .flowOn(mainDispatcher)
companion object {
const val LAST_NON_SILENT_RINGER_MODE_KEY = "key_last_non_silent_ringer_mode"
const val MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME = "quick_affordance_mute_ringer_mode_cache"
private const val DEFAULT_LAST_NON_SILENT_VALUE = AudioManager.RINGER_MODE_NORMAL
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index ea6c107cd161..0d54ab9adb4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -42,7 +42,7 @@ constructor(
override val key: String = BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
- override val pickerName = context.getString(R.string.qr_code_scanner_title)
+ override fun pickerName(): String = context.getString(R.string.qr_code_scanner_title)
override val pickerIconResourceId = R.drawable.ic_qr_code_scanner
@@ -53,6 +53,7 @@ constructor(
override fun onQRCodeScannerActivityChanged() {
trySendWithFailureLogging(state(), TAG)
}
+
override fun onQRCodeScannerPreferenceChanged() {
trySendWithFailureLogging(state(), TAG)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 12270784adca..44e74e7e339b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -51,7 +51,7 @@ constructor(
override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
- override val pickerName: String = context.getString(R.string.accessibility_wallet_button)
+ override fun pickerName(): String = context.getString(R.string.accessibility_wallet_button)
override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index 6f821a2b5228..1ccc689da368 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -62,8 +62,7 @@ constructor(
override val key: String
get() = BuiltInKeyguardQuickAffordanceKeys.VIDEO_CAMERA
- override val pickerName: String
- get() = context.getString(R.string.video_camera)
+ override fun pickerName(): String = context.getString(R.string.video_camera)
override val pickerIconResourceId: Int
get() = R.drawable.ic_videocam
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index ab4abbf3d575..96c94d7d64b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -170,7 +170,7 @@ constructor(
pickerState as? KeyguardQuickAffordanceConfig.PickerScreenState.Disabled
KeyguardQuickAffordancePickerRepresentation(
id = config.key,
- name = config.pickerName,
+ name = config.pickerName(),
iconResourceId = config.pickerIconResourceId,
isEnabled =
pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.Default,
@@ -234,7 +234,9 @@ constructor(
pw.println(" $slotId$selectionText (capacity = $capacity)")
}
pw.println("Available affordances on device:")
- configs.forEach { config -> pw.println(" ${config.key} (\"${config.pickerName}\")") }
+ configs.forEach { config ->
+ pw.println(" ${config.key} (\"${config.pickerName()}\")")
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 316c903eed5b..88ffa8da666b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -28,6 +28,7 @@ import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.CheckBox;
import android.widget.TextView;
@@ -151,6 +152,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mCurrentActivePosition = -1;
}
mStatusIcon.setVisibility(View.GONE);
+ enableFocusPropertyForView(mContainerLayout);
if (mController.isAnyDeviceTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
@@ -250,7 +252,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mController.getColorItemContent());
updateGroupableCheckBox(true, isDeviceDeselectable, device);
updateEndClickArea(device, isDeviceDeselectable);
- setUpContentDescriptionForView(mContainerLayout, false, device);
+ disableFocusPropertyForView(mContainerLayout);
+ setUpContentDescriptionForView(mSeekBar, device);
setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
false /* showProgressBar */, true /* showCheckBox */,
true /* showEndTouchArea */);
@@ -274,7 +277,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mController.getDeselectableMediaDevice(), device);
updateGroupableCheckBox(true, isDeviceDeselectable, device);
updateEndClickArea(device, isDeviceDeselectable);
- setUpContentDescriptionForView(mContainerLayout, false, device);
+ disableFocusPropertyForView(mContainerLayout);
+ setUpContentDescriptionForView(mSeekBar, device);
setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
false /* showProgressBar */, true /* showCheckBox */,
true /* showEndTouchArea */);
@@ -282,7 +286,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
} else {
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
- setUpContentDescriptionForView(mContainerLayout, false, device);
+ disableFocusPropertyForView(mContainerLayout);
+ setUpContentDescriptionForView(mSeekBar, device);
mCurrentActivePosition = position;
setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
false /* showProgressBar */, false /* showCheckBox */,
@@ -390,7 +395,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
View.IMPORTANT_FOR_ACCESSIBILITY_YES);
mEndTouchArea.setBackgroundTintList(
ColorStateList.valueOf(mController.getColorItemBackground()));
- setUpContentDescriptionForView(mEndTouchArea, true, device);
+ setUpContentDescriptionForView(mEndTouchArea, device);
}
private void updateGroupableCheckBox(boolean isSelected, boolean isGroupable,
@@ -471,14 +476,29 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
notifyDataSetChanged();
}
- private void setUpContentDescriptionForView(View view, boolean clickable,
- MediaDevice device) {
- view.setClickable(clickable);
+ private void disableFocusPropertyForView(View view) {
+ view.setFocusable(false);
+ view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+
+ private void enableFocusPropertyForView(View view) {
+ view.setFocusable(true);
+ view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
+
+ private void setUpContentDescriptionForView(View view, MediaDevice device) {
view.setContentDescription(
mContext.getString(device.getDeviceType()
== MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
? R.string.accessibility_bluetooth_name
: R.string.accessibility_cast_name, device.getName()));
+ view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ host.setOnClickListener(null);
+ }
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 444407c2341a..0ce20cdbe63d 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -73,7 +73,7 @@ constructor(
private val pickerNameResourceId = R.string.note_task_button_label
- override val pickerName: String = context.getString(pickerNameResourceId)
+ override fun pickerName(): String = context.getString(pickerNameResourceId)
override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 2709da38a7d8..992b0221068c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -18,16 +18,19 @@ package com.android.systemui.unfold
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
+import android.os.SystemProperties
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
+import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.util.UnfoldOnlyProgressProvider
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
@@ -37,6 +40,7 @@ import dagger.Provides
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Named
+import javax.inject.Provider
import javax.inject.Singleton
@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class])
@@ -91,6 +95,18 @@ class UnfoldTransitionModule {
}
@Provides
+ @Singleton
+ @Named(UNFOLD_ONLY_PROVIDER)
+ fun provideUnfoldOnlyProvider(
+ foldProvider: FoldProvider,
+ @Main executor: Executor,
+ sourceProvider: Optional<UnfoldTransitionProgressProvider>
+ ): Optional<UnfoldTransitionProgressProvider> =
+ sourceProvider.map { provider ->
+ UnfoldOnlyProgressProvider(foldProvider, executor, provider)
+ }
+
+ @Provides
@Named(UNFOLD_STATUS_BAR)
@Singleton
fun provideStatusBarScopedTransitionProvider(
@@ -102,16 +118,35 @@ class UnfoldTransitionModule {
@Singleton
fun provideShellProgressProvider(
config: UnfoldTransitionConfig,
- provider: Optional<UnfoldTransitionProgressProvider>
- ): ShellUnfoldProgressProvider =
- if (config.isEnabled && provider.isPresent) {
- UnfoldProgressProvider(provider.get())
- } else {
- ShellUnfoldProgressProvider.NO_PROVIDER
- }
+ provider: Provider<Optional<UnfoldTransitionProgressProvider>>,
+ @Named(UNFOLD_ONLY_PROVIDER)
+ unfoldOnlyProvider: Provider<Optional<UnfoldTransitionProgressProvider>>
+ ): ShellUnfoldProgressProvider {
+ val resultingProvider =
+ if (config.isEnabled) {
+ // Return unfold only provider to the shell if we don't want to animate tasks during
+ // folding. Shell provider listeners are responsible for animating task bounds.
+ if (ENABLE_FOLD_TASK_ANIMATIONS) {
+ provider
+ } else {
+ unfoldOnlyProvider
+ }
+ } else {
+ null
+ }
+
+ return resultingProvider?.get()?.orElse(null)?.let(::UnfoldProgressProvider)
+ ?: ShellUnfoldProgressProvider.NO_PROVIDER
+ }
@Provides
fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
}
const val UNFOLD_STATUS_BAR = "unfold_status_bar"
+const val UNFOLD_ONLY_PROVIDER = "unfold_only_provider"
+
+// TODO: b/265764985 - tracking bug to clean-up the flag
+// FeatureFlags are not accessible here because it's a global submodule (see GlobalModule.java)
+private val ENABLE_FOLD_TASK_ANIMATIONS =
+ SystemProperties.getBoolean("persist.unfold.enable_fold_tasks_animation", false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
index 2d1e8a830fc9..a93af7dd7450 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
@@ -99,7 +99,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
- verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 13d1e6488d25..c85c7f678536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -28,7 +28,7 @@ import kotlinx.coroutines.yield
@RoboPilotTest
class FakeKeyguardQuickAffordanceConfig(
override val key: String,
- override val pickerName: String = key,
+ private val pickerName: String = key,
override val pickerIconResourceId: Int = 0,
) : KeyguardQuickAffordanceConfig {
@@ -41,6 +41,8 @@ class FakeKeyguardQuickAffordanceConfig(
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
_lockScreenState
+ override fun pickerName(): String = pickerName
+
override fun onTriggered(
expandable: Expandable?,
): OnTriggeredResult {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index ca7c5db478a1..6b5be58b6d03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -189,12 +189,12 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = config1.key,
- name = config1.pickerName,
+ name = config1.pickerName(),
iconResourceId = config1.pickerIconResourceId,
),
KeyguardQuickAffordancePickerRepresentation(
id = config2.key,
- name = config2.pickerName,
+ name = config2.pickerName(),
iconResourceId = config2.pickerIconResourceId,
),
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 895c1cd5a1a1..1d5971ce9adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -435,7 +435,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = homeControls.key,
- name = homeControls.pickerName,
+ name = homeControls.pickerName(),
iconResourceId = homeControls.pickerIconResourceId,
),
),
@@ -469,7 +469,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = quickAccessWallet.key,
- name = quickAccessWallet.pickerName,
+ name = quickAccessWallet.pickerName(),
iconResourceId = quickAccessWallet.pickerIconResourceId,
),
),
@@ -506,7 +506,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = quickAccessWallet.key,
- name = quickAccessWallet.pickerName,
+ name = quickAccessWallet.pickerName(),
iconResourceId = quickAccessWallet.pickerIconResourceId,
),
),
@@ -514,7 +514,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = qrCodeScanner.key,
- name = qrCodeScanner.pickerName,
+ name = qrCodeScanner.pickerName(),
iconResourceId = qrCodeScanner.pickerIconResourceId,
),
),
@@ -570,7 +570,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = quickAccessWallet.key,
- name = quickAccessWallet.pickerName,
+ name = quickAccessWallet.pickerName(),
iconResourceId = quickAccessWallet.pickerIconResourceId,
),
),
@@ -665,7 +665,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
listOf(
KeyguardQuickAffordancePickerRepresentation(
id = quickAccessWallet.key,
- name = quickAccessWallet.pickerName,
+ name = quickAccessWallet.pickerName(),
iconResourceId = quickAccessWallet.pickerIconResourceId,
),
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index c89897c0233c..6d8c9b106881 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -247,6 +247,20 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void onBindViewHolder_bindConnectedRemoteDevice_verifyContentDescriptionNotNull() {
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
+ ImmutableList.of(mMediaDevice2));
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
+ mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mSeekBar.getContentDescription()).isNotNull();
+ assertThat(mViewHolder.mSeekBar.getAccessibilityDelegate()).isNotNull();
+ assertThat(mViewHolder.mContainerLayout.isFocusable()).isFalse();
+ }
+
+ @Test
public void onBindViewHolder_bindSingleConnectedRemoteDevice_verifyView() {
when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
ImmutableList.of());
@@ -334,6 +348,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+ assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
index 4a28cd1de255..56c624565971 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
@@ -4,7 +4,7 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {
- private val listeners = arrayListOf<TransitionProgressListener>()
+ private val listeners = mutableListOf<TransitionProgressListener>()
override fun destroy() {
listeners.clear()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
index d3fdbd94a5ac..3dec45b4ff9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -20,10 +20,9 @@ import android.os.Vibrator
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.util.TestFoldProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
-import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -115,20 +114,4 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {
verify(vibrator).vibrate(any<VibrationEffect>())
}
-
- private class TestFoldProvider : FoldProvider {
- private val listeners = arrayListOf<FoldProvider.FoldCallback>()
-
- override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
- listeners += callback
- }
-
- override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
- listeners -= callback
- }
-
- fun onFoldUpdate(isFolded: Boolean) {
- listeners.forEach { it.onFoldUpdated(isFolded) }
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
index e2aef31b4f10..e461e3f7fb1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -74,6 +74,11 @@ class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionPr
currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.")
}
+ fun clear() {
+ currentRecording = null
+ recordings.clear()
+ }
+
class UnfoldTransitionRecording {
private val progressHistory: MutableList<Float> = arrayListOf()
private var finishingInvocations: Int = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
new file mode 100644
index 000000000000..35df35ccfe9c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.systemui.unfold.util
+
+import com.android.systemui.unfold.updates.FoldProvider
+import java.util.concurrent.Executor
+
+class TestFoldProvider : FoldProvider {
+ private val listeners = arrayListOf<FoldProvider.FoldCallback>()
+
+ override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
+ listeners += callback
+ }
+
+ override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
+ listeners -= callback
+ }
+
+ fun onFoldUpdate(isFolded: Boolean) {
+ listeners.forEach { it.onFoldUpdated(isFolded) }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
new file mode 100644
index 000000000000..4a38fc069d9f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold.util
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.progress.TestUnfoldProgressListener
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
+
+ private val listener = TestUnfoldProgressListener()
+ private val sourceProvider = TestUnfoldTransitionProvider()
+
+ private val foldProvider = TestFoldProvider()
+
+ private lateinit var progressProvider: UnfoldOnlyProgressProvider
+
+ @Before
+ fun setUp() {
+ progressProvider =
+ UnfoldOnlyProgressProvider(foldProvider, MoreExecutors.directExecutor(), sourceProvider)
+
+ progressProvider.addCallback(listener)
+ }
+
+ @Test
+ fun unfolded_unfoldAnimationFinished_propagatesEvents() {
+ foldProvider.onFoldUpdate(isFolded = true)
+ foldProvider.onFoldUpdate(isFolded = false)
+
+ // Unfold animation
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.5f)
+ sourceProvider.onTransitionFinished()
+
+ with(listener.ensureTransitionFinished()) {
+ assertLastProgress(0.5f)
+ }
+ }
+
+ @Test
+ fun unfoldedWithAnimation_foldAnimation_doesNotPropagateEvents() {
+ foldProvider.onFoldUpdate(isFolded = true)
+ foldProvider.onFoldUpdate(isFolded = false)
+ // Unfold animation
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.5f)
+ sourceProvider.onTransitionFinished()
+ listener.clear()
+
+ // Fold animation
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.2f)
+ sourceProvider.onTransitionFinished()
+
+ listener.assertNotStarted()
+ }
+
+ @Test
+ fun unfoldedWithAnimation_foldAnimationSeveralTimes_doesNotPropagateEvents() {
+ foldProvider.onFoldUpdate(isFolded = true)
+ foldProvider.onFoldUpdate(isFolded = false)
+ // Unfold animation
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.5f)
+ sourceProvider.onTransitionFinished()
+ listener.clear()
+
+ // Start and stop fold animation several times
+ repeat(3) {
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.2f)
+ sourceProvider.onTransitionFinished()
+ }
+
+ listener.assertNotStarted()
+ }
+
+ @Test
+ fun unfoldedAgainAfterFolding_propagatesEvents() {
+ foldProvider.onFoldUpdate(isFolded = true)
+ foldProvider.onFoldUpdate(isFolded = false)
+
+ // Unfold animation
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.5f)
+ sourceProvider.onTransitionFinished()
+
+ // Fold animation
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.2f)
+ sourceProvider.onTransitionFinished()
+ foldProvider.onFoldUpdate(isFolded = true)
+
+ listener.clear()
+
+ // Second unfold animation after folding
+ foldProvider.onFoldUpdate(isFolded = false)
+ sourceProvider.onTransitionStarted()
+ sourceProvider.onTransitionProgress(0.1f)
+ sourceProvider.onTransitionFinished()
+
+ with(listener.ensureTransitionFinished()) {
+ assertLastProgress(0.1f)
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 1989bc77583a..8ef2a1bd26c2 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -76,7 +76,7 @@ import java.util.function.Function;
*/
public final class AuthSession implements IBinder.DeathRecipient {
private static final String TAG = "BiometricService/AuthSession";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
/*
* Defined in biometrics.proto
@@ -118,6 +118,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
@VisibleForTesting final IBinder mToken;
// Info to be shown on BiometricDialog when all cookies are returned.
@VisibleForTesting final PromptInfo mPromptInfo;
+ @VisibleForTesting final BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
private final long mRequestId;
private final long mOperationId;
private final int mUserId;
@@ -164,6 +165,32 @@ public final class AuthSession implements IBinder.DeathRecipient {
@NonNull PromptInfo promptInfo,
boolean debugEnabled,
@NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) {
+ this(context, biometricContext, statusBarService, sysuiReceiver, keystore, random,
+ clientDeathReceiver, preAuthInfo, token, requestId, operationId, userId,
+ sensorReceiver, clientReceiver, opPackageName, promptInfo, debugEnabled,
+ fingerprintSensorProperties, BiometricFrameworkStatsLogger.getInstance());
+ }
+
+ @VisibleForTesting
+ AuthSession(@NonNull Context context,
+ @NonNull BiometricContext biometricContext,
+ @NonNull IStatusBarService statusBarService,
+ @NonNull IBiometricSysuiReceiver sysuiReceiver,
+ @NonNull KeyStore keystore,
+ @NonNull Random random,
+ @NonNull ClientDeathReceiver clientDeathReceiver,
+ @NonNull PreAuthInfo preAuthInfo,
+ @NonNull IBinder token,
+ long requestId,
+ long operationId,
+ int userId,
+ @NonNull IBiometricSensorReceiver sensorReceiver,
+ @NonNull IBiometricServiceReceiver clientReceiver,
+ @NonNull String opPackageName,
+ @NonNull PromptInfo promptInfo,
+ boolean debugEnabled,
+ @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties,
+ @NonNull BiometricFrameworkStatsLogger logger) {
Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo);
mContext = context;
mBiometricContext = biometricContext;
@@ -184,6 +211,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
mDebugEnabled = debugEnabled;
mFingerprintSensorProperties = fingerprintSensorProperties;
mCancelled = false;
+ mBiometricFrameworkStatsLogger = logger;
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -708,7 +736,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
- BiometricFrameworkStatsLogger.getInstance().authenticate(
+ mBiometricFrameworkStatsLogger.authenticate(
mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
isCrypto()),
statsModality(),
@@ -723,11 +751,17 @@ public final class AuthSession implements IBinder.DeathRecipient {
} else {
final long latency = System.currentTimeMillis() - mStartTimeMs;
- int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE
- ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON
- : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL
- ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
- : 0;
+ int error = 0;
+ switch(reason) {
+ case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
+ error = BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON;
+ break;
+ case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
+ error = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED;
+ break;
+ default:
+ }
+
if (DEBUG) {
Slog.v(TAG, "Dismissed! Modality: " + statsModality()
+ ", User: " + mUserId
@@ -739,17 +773,19 @@ public final class AuthSession implements IBinder.DeathRecipient {
+ ", Latency: " + latency);
}
// Auth canceled
- BiometricFrameworkStatsLogger.getInstance().error(
- mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
- isCrypto()),
- statsModality(),
- BiometricsProtoEnums.ACTION_AUTHENTICATE,
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- mDebugEnabled,
- latency,
- error,
- 0 /* vendorCode */,
- mUserId);
+ if (error != 0) {
+ mBiometricFrameworkStatsLogger.error(
+ mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
+ isCrypto()),
+ statsModality(),
+ BiometricsProtoEnums.ACTION_AUTHENTICATE,
+ BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ mDebugEnabled,
+ latency,
+ error,
+ 0 /* vendorCode */,
+ mUserId);
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 38445295b0f1..da8eb23cbeae 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -238,6 +238,7 @@ public final class DisplayManagerService extends SystemService {
private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6;
private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
+ private static final int MSG_RECEIVED_DEVICE_STATE = 9;
private static final int[] EMPTY_ARRAY = new int[0];
private static final HdrConversionMode HDR_CONVERSION_MODE_UNSUPPORTED = new HdrConversionMode(
HDR_CONVERSION_UNSUPPORTED);
@@ -3213,6 +3214,10 @@ public final class DisplayManagerService extends SystemService {
mWindowManagerInternal.requestTraversalFromDisplayManager();
break;
+ case MSG_RECEIVED_DEVICE_STATE:
+ mWindowManagerInternal.onDisplayManagerReceivedDeviceState(msg.arg1);
+ break;
+
case MSG_UPDATE_VIEWPORT: {
final boolean changed;
synchronized (mSyncRoot) {
@@ -4680,6 +4685,14 @@ public final class DisplayManagerService extends SystemService {
public void onStateChanged(int deviceState) {
boolean isDeviceStateOverrideActive = deviceState != mBaseState;
synchronized (mSyncRoot) {
+ // Notify WindowManager that we are about to handle new device state, this should
+ // be sent before any work related to the device state in DisplayManager, so
+ // WindowManager could do implement that depends on the device state and display
+ // changes (serializes device state update and display change events)
+ Message msg = mHandler.obtainMessage(MSG_RECEIVED_DEVICE_STATE);
+ msg.arg1 = deviceState;
+ mHandler.sendMessage(msg);
+
mLogicalDisplayMapper
.setDeviceStateLocked(deviceState, isDeviceStateOverrideActive);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
index 873d5fc92601..4bc20a555dec 100644
--- a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
+++ b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
@@ -42,6 +42,12 @@ public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice {
// How long to wait for the audio system to report its capabilities after eARC was connected
static final long REPORT_CAPS_MAX_DELAY_MS = 2_000;
+ // Array containing the names of the eARC states. The integer value of the eARC state
+ // corresponds to the index in the array.
+ private static final String earcStatusNames[] = {"HDMI_EARC_STATUS_IDLE",
+ "HDMI_EARC_STATUS_EARC_PENDING", "HDMI_EARC_STATUS_ARC_PENDING",
+ "HDMI_EARC_STATUS_EARC_CONNECTED"};
+
// eARC Capability Data Structure parameters
private static final int EARC_CAPS_PAYLOAD_LENGTH = 0x02;
private static final int EARC_CAPS_DATA_START = 0x03;
@@ -75,11 +81,17 @@ public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice {
mReportCapsRunnable = new ReportCapsRunnable();
}
+ private String earcStatusToString(int status) {
+ return earcStatusNames[status];
+ }
+
protected void handleEarcStateChange(@Constants.EarcStatus int status) {
int oldEarcStatus;
+
synchronized (mLock) {
- HdmiLogger.debug("eARC state change [old:%b new %b]", mEarcStatus,
- status);
+ HdmiLogger.debug("eARC state change [old: %s(%d) new: %s(%d)]",
+ earcStatusToString(mEarcStatus), mEarcStatus,
+ earcStatusToString(status), status);
oldEarcStatus = mEarcStatus;
mEarcStatus = status;
}
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 2df601fd1d3b..b9f6e177e637 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -68,7 +68,7 @@ class AnrController {
void notifyAppUnresponsive(InputApplicationHandle applicationHandle,
TimeoutRecord timeoutRecord) {
try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyAppUnresponsive()");
+ timeoutRecord.mLatencyTracker.notifyAppUnresponsiveStarted();
timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowStarted();
preDumpIfLockTooSlow();
timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowEnded();
@@ -111,7 +111,6 @@ class AnrController {
if (!blamePendingFocusRequest) {
Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: "
+ timeoutRecord.mReason);
- dumpAnrStateAsync(activity, null /* windowState */, timeoutRecord.mReason);
mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity);
}
}
@@ -123,8 +122,13 @@ class AnrController {
} else {
activity.inputDispatchingTimedOut(timeoutRecord, INVALID_PID);
}
+
+ if (!blamePendingFocusRequest) {
+ dumpAnrStateAsync(activity, null /* windowState */, timeoutRecord.mReason);
+ }
+
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ timeoutRecord.mLatencyTracker.notifyAppUnresponsiveEnded();
}
}
@@ -139,7 +143,7 @@ class AnrController {
void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
@NonNull TimeoutRecord timeoutRecord) {
try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyWindowUnresponsive()");
+ timeoutRecord.mLatencyTracker.notifyWindowUnresponsiveStarted();
if (notifyWindowUnresponsive(token, timeoutRecord)) {
return;
}
@@ -150,7 +154,7 @@ class AnrController {
}
notifyWindowUnresponsive(pid.getAsInt(), timeoutRecord);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ timeoutRecord.mLatencyTracker.notifyWindowUnresponsiveEnded();
}
}
@@ -168,6 +172,7 @@ class AnrController {
final int pid;
final boolean aboveSystem;
final ActivityRecord activity;
+ final WindowState windowState;
timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
synchronized (mService.mGlobalLock) {
timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
@@ -175,7 +180,7 @@ class AnrController {
if (target == null) {
return false;
}
- WindowState windowState = target.getWindowState();
+ windowState = target.getWindowState();
pid = target.getPid();
// Blame the activity if the input token belongs to the window. If the target is
// embedded, then we will blame the pid instead.
@@ -183,13 +188,13 @@ class AnrController {
? windowState.mActivityRecord : null;
Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + timeoutRecord.mReason);
aboveSystem = isWindowAboveSystem(windowState);
- dumpAnrStateAsync(activity, windowState, timeoutRecord.mReason);
}
if (activity != null) {
activity.inputDispatchingTimedOut(timeoutRecord, pid);
} else {
mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, timeoutRecord);
}
+ dumpAnrStateAsync(activity, windowState, timeoutRecord.mReason);
return true;
}
@@ -199,15 +204,10 @@ class AnrController {
private void notifyWindowUnresponsive(int pid, TimeoutRecord timeoutRecord) {
Slog.i(TAG_WM,
"ANR in input window owned by pid=" + pid + ". Reason: " + timeoutRecord.mReason);
- timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
- synchronized (mService.mGlobalLock) {
- timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
- dumpAnrStateAsync(null /* activity */, null /* windowState */, timeoutRecord.mReason);
- }
-
// We cannot determine the z-order of the window, so place the anr dialog as high
// as possible.
mService.mAmInternal.inputDispatchingTimedOut(pid, true /*aboveSystem*/, timeoutRecord);
+ dumpAnrStateAsync(null /* activity */, null /* windowState */, timeoutRecord.mReason);
}
/**
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
index 41c052d95053..31b1069dd022 100644
--- a/services/core/java/com/android/server/wm/DeviceStateController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -19,9 +19,6 @@ package com.android.server.wm;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.devicestate.DeviceStateManager;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.util.ArrayMap;
import com.android.internal.R;
@@ -36,17 +33,15 @@ import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
- * Class that registers callbacks with the {@link DeviceStateManager} and responds to device
+ * Class that listens for a callback from display manager and responds to device state
* changes.
*/
-final class DeviceStateController implements DeviceStateManager.DeviceStateCallback {
+final class DeviceStateController {
// Used to synchronize WindowManager services call paths with DeviceStateManager's callbacks.
@NonNull
private final WindowManagerGlobalLock mWmLock;
@NonNull
- private final DeviceStateManager mDeviceStateManager;
- @NonNull
private final int[] mOpenDeviceStates;
@NonNull
private final int[] mHalfFoldedDeviceStates;
@@ -77,10 +72,8 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
CONCURRENT,
}
- DeviceStateController(@NonNull Context context, @NonNull Handler handler,
- @NonNull WindowManagerGlobalLock wmLock) {
+ DeviceStateController(@NonNull Context context, @NonNull WindowManagerGlobalLock wmLock) {
mWmLock = wmLock;
- mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
mOpenDeviceStates = context.getResources()
.getIntArray(R.array.config_openDeviceStates);
@@ -97,10 +90,6 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
mMatchBuiltInDisplayOrientationToDefaultDisplay = context.getResources()
.getBoolean(R.bool
.config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay);
-
- if (mDeviceStateManager != null) {
- mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
- }
}
/**
@@ -137,8 +126,19 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb
return mMatchBuiltInDisplayOrientationToDefaultDisplay;
}
- @Override
- public void onStateChanged(int state) {
+ /**
+ * This event is sent from DisplayManager just before the device state is applied to
+ * the displays. This is needed to make sure that we first receive this callback before
+ * any device state related display updates from the DisplayManager.
+ *
+ * The flow for this event is the following:
+ * - {@link DeviceStateManager} sends event to {@link android.hardware.display.DisplayManager}
+ * - {@link android.hardware.display.DisplayManager} sends it to {@link WindowManagerInternal}
+ * - {@link WindowManagerInternal} eventually calls this method
+ *
+ * @param state device state as defined by {@link DeviceStateManager}
+ */
+ public void onDeviceStateReceivedByDisplayManager(int state) {
mCurrentState = state;
final DeviceState deviceState;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bc1ddf8f66bc..8be36f07a040 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1730,7 +1730,9 @@ public class DisplayRotation {
}
/**
- * Called by the DeviceStateManager callback when the device state changes.
+ * Called by the display manager just before it applied the device state, it is guaranteed
+ * that in case of physical display change the {@link DisplayRotation#physicalDisplayChanged}
+ * method will be invoked *after* this one.
*/
void foldStateChanged(DeviceStateController.DeviceState deviceState) {
if (mFoldController != null) {
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 78522498987c..f0757db76bda 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -71,7 +71,10 @@ public class PhysicalDisplaySwitchTransitionLauncher {
}
/**
- * Called by the DeviceStateManager callback when the state changes.
+ * Called by the display manager just before it applied the device state, it is guaranteed
+ * that in case of physical display change the
+ * {@link PhysicalDisplaySwitchTransitionLauncher#requestDisplaySwitchTransitionIfNeeded}
+ * method will be invoked *after* this one.
*/
void foldStateChanged(DeviceState newDeviceState) {
boolean isUnfolding = mDeviceState == FOLDED
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7e839596c59b..4995236da1be 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -438,8 +438,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
- mDeviceStateController = new DeviceStateController(service.mContext, service.mH,
- service.mGlobalLock);
+ mDeviceStateController = new DeviceStateController(service.mContext, service.mGlobalLock);
mDisplayRotationCoordinator = new DisplayRotationCoordinator();
}
@@ -1283,6 +1282,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
false /* includingParents */);
}
+ /**
+ * Called just before display manager has applied the device state to the displays
+ * @param deviceState device state as defined by
+ * {@link android.hardware.devicestate.DeviceStateManager}
+ */
+ void onDisplayManagerReceivedDeviceState(int deviceState) {
+ mDeviceStateController.onDeviceStateReceivedByDisplayManager(deviceState);
+ }
+
// TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
DisplayContent getDefaultDisplay() {
return mDefaultDisplay;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 92d4cec80cc9..9e7df004806b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -370,6 +370,13 @@ public abstract class WindowManagerInternal {
public abstract void requestTraversalFromDisplayManager();
/**
+ * Called just before display manager has applied the device state to the displays
+ * @param deviceState device state as defined by
+ * {@link android.hardware.devicestate.DeviceStateManager}
+ */
+ public abstract void onDisplayManagerReceivedDeviceState(int deviceState);
+
+ /**
* Set by the accessibility layer to observe changes in the magnified region,
* rotation, and other window transformations related to display magnification
* as the window manager is responsible for doing the actual magnification
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e33c6f03c720..d84c85c8e823 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7674,6 +7674,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void onDisplayManagerReceivedDeviceState(int deviceState) {
+ mH.post(() -> {
+ synchronized (mGlobalLock) {
+ mRoot.onDisplayManagerReceivedDeviceState(deviceState);
+ }
+ });
+ }
+
+ @Override
public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
synchronized (mGlobalLock) {
if (mAccessibilityController.hasCallbacks()) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 4268eb924225..662477ddbbe9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -18,6 +18,9 @@ package com.android.server.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON;
+import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED;
+import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED;
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
@@ -32,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -45,6 +49,7 @@ import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -64,7 +69,10 @@ import android.security.KeyStore;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
+import com.android.server.biometrics.log.OperationContextExt;
import org.junit.Before;
import org.junit.Test;
@@ -95,6 +103,7 @@ public class AuthSessionTest {
@Mock private IBiometricSysuiReceiver mSysuiReceiver;
@Mock private KeyStore mKeyStore;
@Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
+ @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
private Random mRandom;
private IBinder mToken;
@@ -395,6 +404,84 @@ public class AuthSessionTest {
eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE), eq(acquiredStrVendor));
}
+ @Test
+ public void testLogOnDialogDismissed_authenticatedWithConfirmation() throws RemoteException {
+ final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+
+ setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 0 /* operationId */,
+ 0 /* userId */);
+ session.goToInitialState();
+ assertEquals(STATE_AUTH_CALLED, session.getState());
+
+ session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null);
+ verify(mBiometricFrameworkStatsLogger, times(1)).authenticate(
+ (OperationContextExt) anyObject(),
+ eq(BiometricsProtoEnums.MODALITY_FACE),
+ eq(BiometricsProtoEnums.ACTION_UNKNOWN),
+ eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
+ eq(false), /* debugEnabled */
+ anyLong(), /* latency */
+ eq(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED),
+ eq(true), /* confirmationRequired */
+ eq(0) /* userId */,
+ eq(-1f) /* ambientLightLux */);
+ }
+
+ @Test
+ public void testLogOnDialogDismissed_authenticatedWithoutConfirmation() throws RemoteException {
+ final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+
+ setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 0 /* operationId */,
+ 0 /* userId */);
+ session.goToInitialState();
+ assertEquals(STATE_AUTH_CALLED, session.getState());
+
+ session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null);
+ verify(mBiometricFrameworkStatsLogger, never()).authenticate(
+ anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
+ anyBoolean(), anyInt(), eq(-1f));
+ verify(mBiometricFrameworkStatsLogger, never()).error(
+ anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
+ anyInt(), anyInt());
+ }
+
+ @Test
+ public void testLogOnDialogDismissed_error() throws RemoteException {
+ final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+
+ setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ 0 /* operationId */,
+ 0 /* userId */);
+ session.goToInitialState();
+ assertEquals(STATE_AUTH_CALLED, session.getState());
+
+ session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null);
+ verify(mBiometricFrameworkStatsLogger, times(1)).error(
+ (OperationContextExt) anyObject(),
+ eq(BiometricsProtoEnums.MODALITY_FACE),
+ eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
+ eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
+ eq(false),
+ anyLong(),
+ eq(BIOMETRIC_ERROR_NEGATIVE_BUTTON),
+ eq(0) /* vendorCode */,
+ eq(0) /* userId */);
+ }
+
// TODO (b/208484275) : Enable these tests
// @Test
// public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
@@ -498,7 +585,7 @@ public class AuthSessionTest {
return new AuthSession(mContext, mBiometricContext, mStatusBarService, mSysuiReceiver,
mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId,
operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo,
- false /* debugEnabled */, mFingerprintSensorProps);
+ false /* debugEnabled */, mFingerprintSensorProps, mBiometricFrameworkStatsLogger);
}
private PromptInfo createPromptInfo(@Authenticators.Types int authenticators) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index a6c57371803b..0e775d57bbd7 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -35,12 +35,14 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -72,6 +74,7 @@ import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
@@ -96,17 +99,18 @@ import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import com.android.server.display.DisplayManagerService.DeviceStateListener;
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import com.google.common.collect.ImmutableMap;
-
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+import com.google.common.collect.ImmutableMap;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -114,6 +118,7 @@ import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -547,6 +552,30 @@ public class DisplayManagerServiceTest {
}
/**
+ * Tests that we send the device state to window manager
+ */
+ @Test
+ public void testOnStateChanged_sendsStateChangedEventToWm() throws Exception {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ DeviceStateListener listener = displayManager.new DeviceStateListener();
+ Handler handler = displayManager.getDisplayHandler();
+ IDisplayManagerCallback displayChangesCallback = registerDisplayChangeCallback(
+ displayManager);
+
+ listener.onStateChanged(123);
+ waitForIdleHandler(handler);
+
+ InOrder inOrder = inOrder(mMockWindowManagerInternal, displayChangesCallback);
+ // Verify there are no display events before WM call
+ inOrder.verify(displayChangesCallback, never()).onDisplayEvent(anyInt(), anyInt());
+ inOrder.verify(mMockWindowManagerInternal).onDisplayManagerReceivedDeviceState(123);
+ }
+
+ /**
* Tests that there should be a display change notification to WindowManager to update its own
* internal state for things like display cutout when nonOverrideDisplayInfo is changed.
*/
@@ -2054,6 +2083,15 @@ public class DisplayManagerServiceTest {
updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo);
}
+ private IDisplayManagerCallback registerDisplayChangeCallback(
+ DisplayManagerService displayManager) {
+ IDisplayManagerCallback displayChangesCallback = mock(IDisplayManagerCallback.class);
+ when(displayChangesCallback.asBinder()).thenReturn(new Binder());
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ binderService.registerCallback(displayChangesCallback);
+ return displayChangesCallback;
+ }
+
private FakeDisplayManagerCallback registerDisplayListenerCallback(
DisplayManagerService displayManager,
DisplayManagerService.BinderService displayManagerBinderService,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
index b515a9dc0d6b..07d8b8a0c8df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
@@ -27,7 +27,6 @@ import static org.mockito.ArgumentMatchers.any;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.devicestate.DeviceStateManager;
-import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -73,20 +72,19 @@ public class DeviceStateControllerTests {
};
mBuilder.setDelegate(mDelegate);
mBuilder.build();
- verify(mMockDeviceStateManager).registerCallback(any(), any());
}
@Test
public void testInitialization() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
- mTarget.onStateChanged(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
}
@Test
public void testInitializationWithNoFoldSupport() {
initialize(false /* supportFold */, false /* supportHalfFolded */);
- mTarget.onStateChanged(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
// Note that the folded state is ignored.
assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
}
@@ -94,24 +92,24 @@ public class DeviceStateControllerTests {
@Test
public void testWithFoldSupported() {
initialize(true /* supportFold */, false /* supportHalfFolded */);
- mTarget.onStateChanged(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
- mTarget.onStateChanged(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
- mTarget.onStateChanged(mHalfFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]);
assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
}
@Test
public void testWithHalfFoldSupported() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
- mTarget.onStateChanged(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
- mTarget.onStateChanged(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
- mTarget.onStateChanged(mHalfFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]);
assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
- mTarget.onStateChanged(mConcurrentDisplayState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState);
assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState);
}
@@ -121,15 +119,15 @@ public class DeviceStateControllerTests {
assertEquals(1, mTarget.mDeviceStateCallbacks.size());
assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate));
- mTarget.onStateChanged(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
- mTarget.onStateChanged(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
// The callback should not receive state change when the it is unregistered.
mTarget.unregisterDeviceStateCallback(mDelegate);
assertTrue(mTarget.mDeviceStateCallbacks.isEmpty());
- mTarget.onStateChanged(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, mCurrentState);
}
@@ -195,9 +193,7 @@ public class DeviceStateControllerTests {
Resources mockRes = mock(Resources.class);
when(mMockContext.getResources()).thenReturn((mockRes));
mockFold(mSupportFold, mSupportHalfFold);
- Handler mockHandler = mock(Handler.class);
- mTarget = new DeviceStateController(mMockContext, mockHandler,
- new WindowManagerGlobalLock());
+ mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock());
mTarget.registerDeviceStateCallback(mDelegate, MoreExecutors.directExecutor());
}
}