Merge "Allow the dismiss target size to be overridden" into tm-qpr-dev
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index aeaf6ed..be100bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1270,7 +1270,7 @@
}
final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
- && mExpandedBubble != null;
+ && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null;
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show manage edu: " + shouldShow);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 946dfde..40945bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -123,6 +123,7 @@
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.SplitBounds;
@@ -203,6 +204,8 @@
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ private DefaultMixedHandler mMixedHandler;
+
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@Override
@@ -326,6 +329,10 @@
transitions.addHandler(this);
}
+ public void setMixedHandler(DefaultMixedHandler mixedHandler) {
+ mMixedHandler = mixedHandler;
+ }
+
@VisibleForTesting
SplitScreenTransitions getSplitTransitions() {
return mSplitTransitions;
@@ -1879,8 +1886,25 @@
// Use normal animations.
return false;
+ } else if (mMixedHandler != null && hasDisplayChange(info)) {
+ // A display-change has been un-expectedly inserted into the transition. Redirect
+ // handling to the mixed-handler to deal with splitting it up.
+ if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
+ startTransaction, finishTransaction, finishCallback)) {
+ return true;
+ }
}
+ return startPendingAnimation(transition, info, startTransaction, finishTransaction,
+ finishCallback);
+ }
+
+ /** Starts the pending transition animation. */
+ public boolean startPendingAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(
@@ -1899,6 +1923,15 @@
return true;
}
+ private boolean hasDisplayChange(TransitionInfo info) {
+ boolean has = false;
+ for (int iC = 0; iC < info.getChanges().size() && !has; ++iC) {
+ final TransitionInfo.Change change = info.getChanges().get(iC);
+ has = change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0;
+ }
+ return has;
+ }
+
/** Called to clean-up state and do house-keeping after the animation is done. */
public void onTransitionAnimationComplete() {
// If still playing, let it finish.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index bcf4fbd..3cba929 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -57,6 +58,9 @@
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
+ /** Both the display and split-state (enter/exit) is changing */
+ static final int TYPE_DISPLAY_AND_SPLIT_CHANGE = 2;
+
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -69,6 +73,7 @@
Transitions.TransitionFinishCallback mFinishCallback = null;
Transitions.TransitionHandler mLeftoversHandler = null;
+ WindowContainerTransaction mFinishWCT = null;
/**
* Mixed transitions are made up of multiple "parts". This keeps track of how many
@@ -95,6 +100,9 @@
mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler();
mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler();
mPlayer.addHandler(this);
+ if (mSplitHandler != null) {
+ mSplitHandler.setMixedHandler(this);
+ }
}, this);
}
}
@@ -122,10 +130,12 @@
}
private TransitionInfo subCopy(@NonNull TransitionInfo info,
- @WindowManager.TransitionType int newType) {
- final TransitionInfo out = new TransitionInfo(newType, info.getFlags());
- for (int i = 0; i < info.getChanges().size(); ++i) {
- out.getChanges().add(info.getChanges().get(i));
+ @WindowManager.TransitionType int newType, boolean withChanges) {
+ final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
+ if (withChanges) {
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ out.getChanges().add(info.getChanges().get(i));
+ }
}
out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y);
out.setAnimationOptions(info.getAnimationOptions());
@@ -157,6 +167,8 @@
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
+ return false;
} else {
mActiveTransitions.remove(mixed);
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -173,7 +185,7 @@
+ "entering PIP while Split-Screen is active.");
TransitionInfo.Change pipChange = null;
TransitionInfo.Change wallpaper = null;
- final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK);
+ final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
boolean homeIsOpening = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
TransitionInfo.Change change = info.getChanges().get(i);
@@ -254,6 +266,87 @@
return true;
}
+ private void unlinkMissingParents(TransitionInfo from) {
+ for (int i = 0; i < from.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = from.getChanges().get(i);
+ if (chg.getParent() == null) continue;
+ if (from.getChange(chg.getParent()) == null) {
+ from.getChanges().get(i).setParent(null);
+ }
+ }
+ }
+
+ private boolean isWithinTask(TransitionInfo info, TransitionInfo.Change chg) {
+ TransitionInfo.Change curr = chg;
+ while (curr != null) {
+ if (curr.getTaskInfo() != null) return true;
+ if (curr.getParent() == null) break;
+ curr = info.getChange(curr.getParent());
+ }
+ return false;
+ }
+
+ /**
+ * This is intended to be called by SplitCoordinator as a helper to mix an already-pending
+ * split transition with a display-change. The use-case for this is when a display
+ * change/rotation gets collected into a split-screen enter/exit transition which has already
+ * been claimed by StageCoordinator.handleRequest . This happens during launcher tests.
+ */
+ public boolean animatePendingSplitWithDisplayChange(@NonNull IBinder transition,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT,
+ @NonNull SurfaceControl.Transaction finishT,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final TransitionInfo everythingElse = subCopy(info, info.getType(), true /* withChanges */);
+ final TransitionInfo displayPart = subCopy(info, TRANSIT_CHANGE, false /* withChanges */);
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (isWithinTask(info, change)) continue;
+ displayPart.addChange(change);
+ everythingElse.getChanges().remove(i);
+ }
+ if (displayPart.getChanges().isEmpty()) return false;
+ unlinkMissingParents(everythingElse);
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition);
+ mixed.mFinishCallback = finishCallback;
+ mActiveTransitions.add(mixed);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change "
+ + "and split change.");
+ // We need to split the transition into 2 parts: the split part and the display part.
+ mixed.mInFlightSubAnimations = 2;
+
+ Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+ --mixed.mInFlightSubAnimations;
+ if (wctCB != null) {
+ throw new IllegalArgumentException("Can't mix transitions that require finish"
+ + " sync callback");
+ }
+ if (wct != null) {
+ if (mixed.mFinishWCT == null) {
+ mixed.mFinishWCT = wct;
+ } else {
+ mixed.mFinishWCT.merge(wct, true /* transfer */);
+ }
+ }
+ if (mixed.mInFlightSubAnimations > 0) return;
+ mActiveTransitions.remove(mixed);
+ mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, null /* wctCB */);
+ };
+
+ // Dispatch the display change. This will most-likely be taken by the default handler.
+ // Do this first since the first handler used will apply the startT; the display change
+ // needs to take a screenshot before that happens so we need it to be the first handler.
+ mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, displayPart,
+ startT, finishT, finishCB, mSplitHandler);
+
+ // Note: at this point, startT has probably already been applied, so we are basically
+ // giving splitHandler an empty startT. This is currently OK because display-change will
+ // grab a screenshot and paste it on top anyways.
+ mSplitHandler.startPendingAnimation(
+ transition, everythingElse, startT, finishT, finishCB);
+ return true;
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -279,6 +372,8 @@
} else {
mPipHandler.end();
}
+ } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
+ // queue
} else {
throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ mixed.mType);
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 7d4dcf8..ebabdf57 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -352,8 +352,11 @@
* The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
* this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
* before the cancellation.
+ *
+ * If this launch animation affected the occlusion state of the keyguard, WM will provide
+ * us with [newKeyguardOccludedState] so that we can set the occluded state appropriately.
*/
- fun onLaunchAnimationCancelled() {}
+ fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
}
@VisibleForTesting
@@ -667,7 +670,7 @@
removeTimeout()
context.mainExecutor.execute {
animation?.cancel()
- controller.onLaunchAnimationCancelled()
+ controller.onLaunchAnimationCancelled(newKeyguardOccludedState = isKeyguardOccluded)
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index eac5d275..9656b8a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -238,7 +238,7 @@
}
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
controller.onLaunchAnimationCancelled()
enableDialogDismiss()
dialog.dismiss()
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml b/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml
new file mode 100644
index 0000000..732fc57
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="6dp"
+ android:insetBottom="6dp">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/textColorSecondary"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="24dp"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentTertiary"
+ android:width="1dp"
+ />
+ <padding android:left="16dp"
+ android:top="12dp"
+ android:right="16dp"
+ android:bottom="12dp"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
index db508c9..67fd5b9 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
@@ -17,15 +17,17 @@
*/
-->
-<!-- This contains disable esim buttonas shared by sim_pin/sim_puk screens -->
-<com.android.keyguard.KeyguardEsimArea
- xmlns:android="http://schemas.android.com/apk/res/android"
+<!-- This contains disable eSim buttons shared by sim_pin/sim_puk screens -->
+<com.android.keyguard.KeyguardEsimArea xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_disable_esim"
+ style="@style/Keyguard.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/keyguard_disable_esim"
- android:visibility="gone"
+ android:background="@drawable/kg_bouncer_secondary_button"
+ android:drawablePadding="10dp"
+ android:drawableStart="@drawable/ic_no_sim"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/disable_carrier_button_text"
- style="@style/Keyguard.TextView"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/kg_status_line_font_size"
- android:textAllCaps="@bool/kg_use_all_caps" />
+ android:visibility="gone" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index f2fe520..7db0fe9 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -47,8 +47,7 @@
<include layout="@layout/keyguard_esim_area"
android:id="@+id/keyguard_esim_area"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/eca_overlap" />
+ android:layout_height="wrap_content" />
<RelativeLayout
android:id="@+id/row0"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index a21ec29..422bd4c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -51,8 +51,7 @@
<include layout="@layout/keyguard_esim_area"
android:id="@+id/keyguard_esim_area"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/eca_overlap" />
+ android:layout_height="wrap_content" />
<RelativeLayout
android:id="@+id/row0"
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index e80cfaf..a1068c6 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -31,8 +31,4 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">100dp</dimen>
<dimen name="widget_label_font_size">18sp</dimen>
-
- <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
- Should be 0 on devices with plenty of room (e.g. tablets) -->
- <dimen name="eca_overlap">0dip</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index ac131ae..46f6ab2 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -44,10 +44,6 @@
<dimen name="keyguard_eca_top_margin">18dp</dimen>
<dimen name="keyguard_eca_bottom_margin">12dp</dimen>
- <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
- Should be 0 on devices with plenty of room (e.g. tablets) -->
- <dimen name="eca_overlap">-10dip</dimen>
-
<!-- Slice header -->
<dimen name="widget_title_font_size">20dp</dimen>
<dimen name="widget_title_line_height">24dp</dimen>
diff --git a/packages/SystemUI/res/drawable/ic_no_sim.xml b/packages/SystemUI/res/drawable/ic_no_sim.xml
new file mode 100644
index 0000000..ccfb6ea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_no_sim.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,17.175 L18,15.175V4Q18,4 18,4Q18,4 18,4H10.85L8.85,6L7.4,4.6L10,2H18Q18.825,2 19.413,2.587Q20,3.175 20,4ZM20.5,23.3 L6,8.8V20Q6,20 6,20Q6,20 6,20H18Q18,20 18,20Q18,20 18,20V17.975L20,19.975V20Q20,20.825 19.413,21.413Q18.825,22 18,22H6Q5.175,22 4.588,21.413Q4,20.825 4,20V8L4.6,7.4L0.7,3.5L2.125,2.1L21.9,21.875ZM13.525,10.675Q13.525,10.675 13.525,10.675Q13.525,10.675 13.525,10.675ZM11.65,14.475Q11.65,14.475 11.65,14.475Q11.65,14.475 11.65,14.475Q11.65,14.475 11.65,14.475Q11.65,14.475 11.65,14.475Z"/>
+</vector>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a802723..37549c9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -611,6 +611,33 @@
2 - Override the setting to never bypass keyguard -->
<integer name="config_face_unlock_bypass_override">0</integer>
+ <!-- Messages that should NOT be shown to the user during face authentication on keyguard.
+ This includes both lockscreen and bouncer. This should be used to hide messages that may be
+ too chatty or messages that the user can't do much about. Entries are defined in
+ android.hardware.biometrics.face@1.0 types.hal.
+
+ Although not visibly shown to the user, these acquired messages (sent per face auth frame)
+ are still counted towards the total frames to determine whether a deferred message
+ (see config_face_help_msgs_defer_until_timeout) meets the threshold % of frames to show on
+ face timeout. -->
+ <integer-array name="config_face_acquire_device_entry_ignorelist" translatable="false" >
+ </integer-array>
+
+ <!-- Which face help messages to defer until face auth times out. If face auth is cancelled
+ or ends on another error, then the message is never surfaced. May also never surface
+ if it doesn't meet a threshold % of authentication frames specified by.
+ config_face_help_msgs_defer_until_timeout_threshold. -->
+ <integer-array name="config_face_help_msgs_defer_until_timeout">
+ </integer-array>
+
+ <!-- Percentage of face auth frames received required to show a deferred message at
+ FACE_ERROR_TIMEOUT. See config_face_help_msgs_defer_until_timeout for messages
+ that are deferred.-->
+ <item name="config_face_help_msgs_defer_until_timeout_threshold"
+ translatable="false" format="float" type="dimen">
+ .75
+ </item>
+
<!-- Which face help messages to surface when fingerprint is also enrolled.
Message ids correspond with the acquired ids in BiometricFaceConstants -->
<integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
new file mode 100644
index 0000000..b4bfca1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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.keyguard
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+import androidx.core.graphics.drawable.DrawableCompat
+import com.android.systemui.R
+
+abstract class KeyguardSimInputView(context: Context, attrs: AttributeSet) :
+ KeyguardPinBasedInputView(context, attrs) {
+ private var simImageView: ImageView? = null
+ private var disableESimButton: KeyguardEsimArea? = null
+
+ override fun onFinishInflate() {
+ simImageView = findViewById(R.id.keyguard_sim)
+ disableESimButton = findViewById(R.id.keyguard_esim_area)
+ super.onFinishInflate()
+ }
+
+ /** Set UI state based on whether there is a locked eSim card */
+ fun setESimLocked(isESimLocked: Boolean, subId: Int) {
+ disableESimButton?.setSubscriptionId(subId)
+ disableESimButton?.visibility = if (isESimLocked) VISIBLE else GONE
+ simImageView?.visibility = if (isESimLocked) GONE else VISIBLE
+ }
+
+ override fun reloadColors() {
+ super.reloadColors()
+ val customAttrs = intArrayOf(android.R.attr.textColorSecondary)
+ val a = context.obtainStyledAttributes(customAttrs)
+ val imageColor = a.getColor(0, 0)
+ a.recycle()
+ simImageView?.let {
+ val wrappedDrawable = DrawableCompat.wrap(it.drawable)
+ DrawableCompat.setTint(wrappedDrawable, imageColor)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index ae9d3df..9d17015 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -18,21 +18,14 @@
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.core.graphics.drawable.DrawableCompat;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
-public class KeyguardSimPinView extends KeyguardPinBasedInputView {
- private ImageView mSimImageView;
+public class KeyguardSimPinView extends KeyguardSimInputView {
public static final String TAG = "KeyguardSimPinView";
public KeyguardSimPinView(Context context) {
@@ -43,12 +36,6 @@
super(context, attrs);
}
- public void setEsimLocked(boolean locked, int subscriptionId) {
- KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setSubscriptionId(subscriptionId);
- esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -68,7 +55,6 @@
@Override
protected void onFinishInflate() {
- mSimImageView = findViewById(R.id.keyguard_sim);
super.onFinishInflate();
if (mEcaView instanceof EmergencyCarrierArea) {
@@ -86,17 +72,4 @@
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
}
-
- @Override
- public void reloadColors() {
- super.reloadColors();
-
- int[] customAttrs = {android.R.attr.textColorSecondary};
- TypedArray a = getContext().obtainStyledAttributes(customAttrs);
- int imageColor = a.getColor(0, 0);
- a.recycle();
- Drawable wrappedDrawable = DrawableCompat.wrap(mSimImageView.getDrawable());
- DrawableCompat.setTint(wrappedDrawable, imageColor);
- }
}
-
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index ecd88e6..76f7d78 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -105,7 +105,7 @@
showDefaultMessage();
}
- mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
+ mView.setESimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index c0971bf..5f45fe3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -19,13 +19,8 @@
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
-import android.widget.ImageView;
-
-import androidx.core.graphics.drawable.DrawableCompat;
import com.android.systemui.R;
@@ -33,8 +28,7 @@
/**
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
-public class KeyguardSimPukView extends KeyguardPinBasedInputView {
- private ImageView mSimImageView;
+public class KeyguardSimPukView extends KeyguardSimInputView {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
@@ -86,7 +80,6 @@
@Override
protected void onFinishInflate() {
- mSimImageView = findViewById(R.id.keyguard_sim);
super.onFinishInflate();
if (mEcaView instanceof EmergencyCarrierArea) {
@@ -104,18 +97,4 @@
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_puk_unlock);
}
-
- @Override
- public void reloadColors() {
- super.reloadColors();
-
- int[] customAttrs = {android.R.attr.textColorSecondary};
- TypedArray a = getContext().obtainStyledAttributes(customAttrs);
- int imageColor = a.getColor(0, 0);
- a.recycle();
- Drawable wrappedDrawable = DrawableCompat.wrap(mSimImageView.getDrawable());
- DrawableCompat.setTint(wrappedDrawable, imageColor);
- }
}
-
-
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 203f9b6..d8cffd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -30,7 +30,6 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -173,11 +172,9 @@
if (mShowDefaultMessage) {
showDefaultMessage();
}
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
- KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
- esimButton.setSubscriptionId(mSubId);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ mView.setESimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
+
mPasswordEntry.requestFocus();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 763b29e..32c1cf9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -157,6 +157,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -167,6 +168,7 @@
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -281,6 +283,7 @@
private final AuthController mAuthController;
private final StatusBarStateController mStatusBarStateController;
private final UiEventLogger mUiEventLogger;
+ private final Set<Integer> mFaceAcquiredInfoIgnoreList;
private int mStatusBarState;
private final StatusBarStateController.StateListener mStatusBarStateControllerListener =
new StatusBarStateController.StateListener() {
@@ -1023,6 +1026,7 @@
private void handleFaceAuthFailed() {
Assert.isMainThread();
+ mLogger.d("onFaceAuthFailed");
mFaceCancelSignal = null;
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1639,6 +1643,9 @@
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ if (mFaceAcquiredInfoIgnoreList.contains(helpMsgId)) {
+ return;
+ }
handleFaceHelp(helpMsgId, helpString.toString());
}
@@ -1931,6 +1938,11 @@
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
mWakeOnFingerprintAcquiredStart = context.getResources()
.getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
+ mFaceAcquiredInfoIgnoreList = Arrays.stream(
+ mContext.getResources().getIntArray(
+ R.array.config_face_acquire_device_entry_ignorelist))
+ .boxed()
+ .collect(Collectors.toSet());
mHandler = new Handler(mainLooper) {
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
new file mode 100644
index 0000000..2c2ab7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.keyguard.logging
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.dagger.BiometricMessagesLog
+import javax.inject.Inject
+
+/** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */
+@SysUISingleton
+class FaceMessageDeferralLogger
+@Inject
+constructor(@BiometricMessagesLog private val logBuffer: LogBuffer) :
+ BiometricMessageDeferralLogger(logBuffer, "FaceMessageDeferralLogger")
+
+open class BiometricMessageDeferralLogger(
+ private val logBuffer: LogBuffer,
+ private val tag: String
+) {
+ fun reset() {
+ logBuffer.log(tag, DEBUG, "reset")
+ }
+
+ fun logUpdateMessage(acquiredInfo: Int, helpString: String) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ int1 = acquiredInfo
+ str1 = helpString
+ },
+ { "updateMessage acquiredInfo=$int1 helpString=$str1" }
+ )
+ }
+
+ fun logFrameProcessed(
+ acquiredInfo: Int,
+ totalFrames: Int,
+ mostFrequentAcquiredInfoToDefer: String? // may not meet the threshold
+ ) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ int1 = acquiredInfo
+ int2 = totalFrames
+ str1 = mostFrequentAcquiredInfoToDefer
+ },
+ {
+ "frameProcessed acquiredInfo=$int1 totalFrames=$int2 " +
+ "messageToShowOnTimeout=$str1"
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
deleted file mode 100644
index f2d8aaa..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 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.biometrics
-
-/**
- * Provides whether an acquired error message should be shown immediately when its received (see
- * [shouldDefer]) or should be shown when the biometric error is received [getDeferredMessage].
- * @property excludedMessages messages that are excluded from counts
- * @property messagesToDefer messages that shouldn't show immediately when received, but may be
- * shown later if the message is the most frequent message processed and meets [THRESHOLD]
- * percentage of all messages (excluding [excludedMessages])
- */
-class BiometricMessageDeferral(
- private val excludedMessages: Set<Int>,
- private val messagesToDefer: Set<Int>
-) {
- private val msgCounts: MutableMap<Int, Int> = HashMap() // msgId => frequency of msg
- private val msgIdToCharSequence: MutableMap<Int, CharSequence> = HashMap() // msgId => message
- private var totalRelevantMessages = 0
- private var mostFrequentMsgIdToDefer: Int? = null
-
- /** Reset all saved counts. */
- fun reset() {
- totalRelevantMessages = 0
- msgCounts.clear()
- msgIdToCharSequence.clear()
- }
-
- /** Whether the given message should be deferred instead of being shown immediately. */
- fun shouldDefer(acquiredMsgId: Int): Boolean {
- return messagesToDefer.contains(acquiredMsgId)
- }
-
- /**
- * Adds the acquiredMsgId to the counts if it's not in [excludedMessages]. We still count
- * messages that shouldn't be deferred in these counts.
- */
- fun processMessage(acquiredMsgId: Int, helpString: CharSequence) {
- if (excludedMessages.contains(acquiredMsgId)) {
- return
- }
-
- totalRelevantMessages++
- msgIdToCharSequence[acquiredMsgId] = helpString
-
- val newAcquiredMsgCount = msgCounts.getOrDefault(acquiredMsgId, 0) + 1
- msgCounts[acquiredMsgId] = newAcquiredMsgCount
- if (
- messagesToDefer.contains(acquiredMsgId) &&
- (mostFrequentMsgIdToDefer == null ||
- newAcquiredMsgCount > msgCounts.getOrDefault(mostFrequentMsgIdToDefer!!, 0))
- ) {
- mostFrequentMsgIdToDefer = acquiredMsgId
- }
- }
-
- /**
- * Get the most frequent deferred message that meets the [THRESHOLD] percentage of processed
- * messages excluding [excludedMessages].
- * @return null if no messages have been deferred OR deferred messages didn't meet the
- * [THRESHOLD] percentage of messages to show.
- */
- fun getDeferredMessage(): CharSequence? {
- mostFrequentMsgIdToDefer?.let {
- if (msgCounts.getOrDefault(it, 0) > (THRESHOLD * totalRelevantMessages)) {
- return msgIdToCharSequence[mostFrequentMsgIdToDefer]
- }
- }
-
- return null
- }
- companion object {
- const val THRESHOLD = .5f
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
new file mode 100644
index 0000000..fabc1c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 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.biometrics
+
+import android.content.res.Resources
+import com.android.keyguard.logging.BiometricMessageDeferralLogger
+import com.android.keyguard.logging.FaceMessageDeferralLogger
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import java.io.PrintWriter
+import java.util.*
+import javax.inject.Inject
+
+/**
+ * Provides whether a face acquired help message should be shown immediately when its received or
+ * should be shown when face auth times out. See [updateMessage] and [getDeferredMessage].
+ */
+@SysUISingleton
+class FaceHelpMessageDeferral
+@Inject
+constructor(
+ @Main resources: Resources,
+ logBuffer: FaceMessageDeferralLogger,
+ dumpManager: DumpManager
+) :
+ BiometricMessageDeferral(
+ resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
+ resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
+ logBuffer,
+ dumpManager
+ )
+
+/**
+ * @property messagesToDefer messages that shouldn't show immediately when received, but may be
+ * shown later if the message is the most frequent acquiredInfo processed and meets [threshold]
+ * percentage of all passed acquired frames.
+ */
+open class BiometricMessageDeferral(
+ private val messagesToDefer: Set<Int>,
+ private val threshold: Float,
+ private val logBuffer: BiometricMessageDeferralLogger,
+ dumpManager: DumpManager
+) : Dumpable {
+ private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
+ private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
+ private var mostFrequentAcquiredInfoToDefer: Int? = null
+ private var totalFrames = 0
+
+ init {
+ dumpManager.registerDumpable(this.javaClass.name, this)
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("messagesToDefer=$messagesToDefer")
+ pw.println("totalFrames=$totalFrames")
+ pw.println("threshold=$threshold")
+ }
+
+ /** Reset all saved counts. */
+ fun reset() {
+ totalFrames = 0
+ mostFrequentAcquiredInfoToDefer = null
+ acquiredInfoToFrequency.clear()
+ acquiredInfoToHelpString.clear()
+ logBuffer.reset()
+ }
+
+ /** Updates the message associated with the acquiredInfo if it's a message we may defer. */
+ fun updateMessage(acquiredInfo: Int, helpString: String) {
+ if (!messagesToDefer.contains(acquiredInfo)) {
+ return
+ }
+ if (!Objects.equals(acquiredInfoToHelpString[acquiredInfo], helpString)) {
+ logBuffer.logUpdateMessage(acquiredInfo, helpString)
+ acquiredInfoToHelpString[acquiredInfo] = helpString
+ }
+ }
+
+ /** Whether the given message should be deferred instead of being shown immediately. */
+ fun shouldDefer(acquiredMsgId: Int): Boolean {
+ return messagesToDefer.contains(acquiredMsgId)
+ }
+
+ /** Adds the acquiredInfo frame to the counts. We account for all frames. */
+ fun processFrame(acquiredInfo: Int) {
+ if (messagesToDefer.isEmpty()) {
+ return
+ }
+
+ totalFrames++
+
+ val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
+ acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
+ if (
+ messagesToDefer.contains(acquiredInfo) &&
+ (mostFrequentAcquiredInfoToDefer == null ||
+ newAcquiredInfoCount >
+ acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0))
+ ) {
+ mostFrequentAcquiredInfoToDefer = acquiredInfo
+ }
+
+ logBuffer.logFrameProcessed(
+ acquiredInfo,
+ totalFrames,
+ mostFrequentAcquiredInfoToDefer?.toString()
+ )
+ }
+
+ /**
+ * Get the most frequent deferred message that meets the [threshold] percentage of processed
+ * frames.
+ * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the
+ * [threshold] percentage.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ mostFrequentAcquiredInfoToDefer?.let {
+ if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
+ return acquiredInfoToHelpString[it]
+ }
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index bd75ab2..6f38f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -241,9 +241,8 @@
*/
@VisibleForTesting
var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
- private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
+ private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
- private var surfaceBehindParams: SyncRtSurfaceTransactionApplier.SurfaceParams? = null
/**
* Alpha value applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
@@ -458,7 +457,7 @@
* (fingerprint, tap, etc.) and the keyguard is going away.
*/
fun notifyStartSurfaceBehindRemoteAnimation(
- target: RemoteAnimationTarget,
+ targets: Array<RemoteAnimationTarget>,
startTime: Long,
requestedShowSurfaceBehindKeyguard: Boolean
) {
@@ -467,10 +466,7 @@
keyguardViewController.viewRootImpl.view)
}
- // New animation, new params.
- surfaceBehindParams = null
-
- surfaceBehindRemoteAnimationTarget = target
+ surfaceBehindRemoteAnimationTargets = targets
surfaceBehindRemoteAnimationStartTime = startTime
// If we specifically requested that the surface behind be made visible (vs. it being made
@@ -597,7 +593,7 @@
* keyguard dismiss amount and the method of dismissal.
*/
private fun updateSurfaceBehindAppearAmount() {
- if (surfaceBehindRemoteAnimationTarget == null) {
+ if (surfaceBehindRemoteAnimationTargets == null) {
return
}
@@ -715,63 +711,60 @@
* cancelled).
*/
fun setSurfaceBehindAppearAmount(amount: Float) {
- if (surfaceBehindRemoteAnimationTarget == null) {
- return
- }
+ surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget ->
+ val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
- // Otherwise, animate in the surface's scale/transltion.
- val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
+ var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
+ (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
+ MathUtils.clamp(amount, 0f, 1f))
- var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
- (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
- MathUtils.clamp(amount, 0f, 1f))
-
- // If we're dismissing via swipe to the Launcher, we'll play in-window scale animations, so
- // don't also scale the window.
- if (keyguardStateController.isDismissingFromSwipe &&
- willUnlockWithInWindowLauncherAnimations) {
- scaleFactor = 1f
- }
-
- // Scale up from a point at the center-bottom of the surface.
- surfaceBehindMatrix.setScale(
- scaleFactor,
- scaleFactor,
- surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
- surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
- )
-
- // Translate up from the bottom.
- surfaceBehindMatrix.postTranslate(
- 0f,
- surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
- )
-
- // If we're snapping the keyguard back, immediately begin fading it out.
- val animationAlpha =
- if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
- else surfaceBehindAlpha
-
- // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is unable
- // to draw
- val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget?.leash
- if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
- sc?.isValid == true) {
- with(SurfaceControl.Transaction()) {
- setMatrix(sc, surfaceBehindMatrix, tmpFloat)
- setCornerRadius(sc, roundedCornerRadius)
- setAlpha(sc, animationAlpha)
- apply()
+ // If we're dismissing via swipe to the Launcher, we'll play in-window scale animations,
+ // so don't also scale the window.
+ if (keyguardStateController.isDismissingFromSwipe &&
+ willUnlockWithInWindowLauncherAnimations) {
+ scaleFactor = 1f
}
- } else {
- applyParamsToSurface(
- SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
- .withMatrix(surfaceBehindMatrix)
- .withCornerRadius(roundedCornerRadius)
- .withAlpha(animationAlpha)
- .build()
+
+ // Translate up from the bottom.
+ surfaceBehindMatrix.setTranslate(
+ surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
+ surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
)
+
+ // Scale up from a point at the center-bottom of the surface.
+ surfaceBehindMatrix.postScale(
+ scaleFactor,
+ scaleFactor,
+ keyguardViewController.viewRootImpl.width / 2f,
+ surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
+ )
+
+ // If we're snapping the keyguard back, immediately begin fading it out.
+ val animationAlpha =
+ if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
+ else surfaceBehindAlpha
+
+ // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
+ // unable to draw
+ val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash
+ if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
+ sc?.isValid == true) {
+ with(SurfaceControl.Transaction()) {
+ setMatrix(sc, surfaceBehindMatrix, tmpFloat)
+ setCornerRadius(sc, roundedCornerRadius)
+ setAlpha(sc, animationAlpha)
+ apply()
+ }
+ } else {
+ applyParamsToSurface(
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceBehindRemoteAnimationTarget.leash)
+ .withMatrix(surfaceBehindMatrix)
+ .withCornerRadius(roundedCornerRadius)
+ .withAlpha(animationAlpha)
+ .build()
+ )
+ }
}
}
@@ -796,8 +789,7 @@
launcherUnlockController?.setUnlockAmount(1f, false /* forceIfAnimating */)
// That target is no longer valid since the animation finished, null it out.
- surfaceBehindRemoteAnimationTarget = null
- surfaceBehindParams = null
+ surfaceBehindRemoteAnimationTargets = null
playingCannedUnlockAnimation = false
willUnlockWithInWindowLauncherAnimations = false
@@ -829,7 +821,6 @@
private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) {
surfaceTransactionApplier!!.scheduleApply(params)
- surfaceBehindParams = params
}
private fun fadeInSurfaceBehind() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d4e0f061..be1d162 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -831,7 +831,7 @@
public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {}
@Override
- public void onLaunchAnimationCancelled() {
+ public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) {
Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: "
+ mOccluded);
}
@@ -2587,18 +2587,10 @@
mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("DismissPanel"));
- // Apply the opening animation on root task if exists
- RemoteAnimationTarget aniTarget = apps[0];
- for (RemoteAnimationTarget tmpTarget : apps) {
- if (tmpTarget.taskId != -1 && !tmpTarget.hasAnimatingParent) {
- aniTarget = tmpTarget;
- break;
- }
- }
// Pass the surface and metadata to the unlock animation controller.
mKeyguardUnlockAnimationControllerLazy.get()
.notifyStartSurfaceBehindRemoteAnimation(
- aniTarget, startTime, mSurfaceBehindRemoteAnimationRequested);
+ apps, startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RemoteAnimationDisabled"));
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java
new file mode 100644
index 0000000..7f1ad6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.log.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link com.android.systemui.log.LogBuffer} for BiometricMessages processing such as
+ * {@link com.android.systemui.biometrics.FaceHelpMessageDeferral}
+ */
+@Qualifier
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BiometricMessagesLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index c2a8764..756feb0 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -287,6 +287,17 @@
}
/**
+ * Provides a {@link LogBuffer} for use by
+ * {@link com.android.systemui.biometrics.FaceHelpMessageDeferral}.
+ */
+ @Provides
+ @SysUISingleton
+ @BiometricMessagesLog
+ public static LogBuffer provideBiometricMessagesLogBuffer(LogBufferFactory factory) {
+ return factory.create("BiometricMessagesLog", 150);
+ }
+
+ /**
* Provides a {@link LogBuffer} for use by the status bar network controller.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 408c61f..e06c977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,9 +19,6 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
-import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_FIRST_FRAME_RECEIVED;
-import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.view.View.GONE;
@@ -53,6 +50,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -82,7 +80,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
-import com.android.systemui.biometrics.BiometricMessageDeferral;
+import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -184,6 +182,7 @@
private long mChargingTimeRemaining;
private String mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
+ private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
private boolean mInited;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -233,7 +232,8 @@
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
- AccessibilityManager accessibilityManager) {
+ AccessibilityManager accessibilityManager,
+ FaceHelpMessageDeferral faceHelpMessageDeferral) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -254,6 +254,7 @@
mScreenLifecycle = screenLifecycle;
mScreenLifecycle.addObserver(mScreenObserver);
+ mFaceAcquiredMessageDeferral = faceHelpMessageDeferral;
mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
int[] msgIds = context.getResources().getIntArray(
com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
@@ -1041,8 +1042,22 @@
}
@Override
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo) {
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.processFrame(acquireInfo);
+ }
+ }
+
+ @Override
public void onBiometricHelp(int msgId, String helpString,
BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.updateMessage(msgId, helpString);
+ if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
+ return;
+ }
+ }
+
// TODO(b/141025588): refactor to reduce repetition of code/comments
// Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
// as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
@@ -1053,17 +1068,6 @@
return;
}
- if (biometricSourceType == FACE) {
- if (msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED) {
- mFaceAcquiredMessageDeferral.reset();
- } else {
- mFaceAcquiredMessageDeferral.processMessage(msgId, helpString);
- if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
- return;
- }
- }
- }
-
final boolean faceAuthSoftError = biometricSourceType == FACE
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
@@ -1109,11 +1113,23 @@
}
@Override
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.reset();
+ }
+ }
+
+ @Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
CharSequence deferredFaceMessage = null;
if (biometricSourceType == FACE) {
- deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ if (msgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
+ deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ if (DEBUG) {
+ Log.d(TAG, "showDeferredFaceMessage msgId=" + deferredFaceMessage);
+ }
+ }
mFaceAcquiredMessageDeferral.reset();
}
@@ -1308,14 +1324,4 @@
}
}
};
-
- private final BiometricMessageDeferral mFaceAcquiredMessageDeferral =
- new BiometricMessageDeferral(
- Set.of(
- FACE_ACQUIRED_GOOD,
- FACE_ACQUIRED_START,
- FACE_ACQUIRED_FIRST_FRAME_RECEIVED
- ),
- Set.of(FACE_ACQUIRED_TOO_DARK)
- );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 31b21c9..553826d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -136,7 +136,7 @@
headsUpManager.removeNotification(notificationKey, true /* releaseImmediately */, animate)
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
notificationShadeWindowViewController.setExpandAnimationRunning(false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 820f6e4..0b63bbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1737,13 +1737,18 @@
}
@Override
- public void onLaunchAnimationCancelled() {
+ public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) {
+ if (newKeyguardOccludedState != null) {
+ mKeyguardViewMediator.setOccluded(
+ newKeyguardOccludedState, false /* animate */);
+ }
+
// Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
// animation so that we can assume that mIsLaunchingActivityOverLockscreen
// being true means that we will collapse the shade (or at least run the
// post collapse runnables) later on.
CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = false;
- getDelegate().onLaunchAnimationCancelled();
+ getDelegate().onLaunchAnimationCancelled(newKeyguardOccludedState);
}
};
} else if (dismissShade) {
@@ -3623,6 +3628,7 @@
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
mKeyguardBypassController.onStartedGoingToSleep();
+ mStatusBarTouchableRegionManager.updateTouchableRegion();
// The unlocked screen off and fold to aod animations might use our LightRevealScrim -
// we need to be expanded for it to be visible.
@@ -3651,6 +3657,7 @@
// once we fully woke up.
updateRevealEffect(true /* wakingUp */);
updateNotificationPanelTouchState();
+ mStatusBarTouchableRegionManager.updateTouchableRegion();
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index c092216..ee948c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -51,7 +51,7 @@
centralSurfaces.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
delegate.onLaunchAnimationCancelled()
centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(false)
centralSurfaces.onLaunchAnimationCancelled(isLaunchForActivity)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 75dac1a..d9c0293 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -55,6 +55,7 @@
private final Context mContext;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
private boolean mShouldAdjustInsets = false;
@@ -72,7 +73,8 @@
Context context,
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
- HeadsUpManagerPhone headsUpManager
+ HeadsUpManagerPhone headsUpManager,
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController
) {
mContext = context;
initResources();
@@ -115,6 +117,8 @@
mNotificationShadeWindowController.setForcePluginOpenListener((forceOpen) -> {
updateTouchableRegion();
});
+
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
}
protected void setup(
@@ -179,7 +183,7 @@
/**
* Set the touchable portion of the status bar based on what elements are visible.
*/
- private void updateTouchableRegion() {
+ public void updateTouchableRegion() {
boolean hasCutoutInset = (mNotificationShadeWindowView != null)
&& (mNotificationShadeWindowView.getRootWindowInsets() != null)
&& (mNotificationShadeWindowView.getRootWindowInsets().getDisplayCutout() != null);
@@ -242,12 +246,25 @@
touchableRegion.union(bounds);
}
+ /**
+ * Helper to let us know when calculating the region is not needed because we know the entire
+ * screen needs to be touchable.
+ */
+ private boolean shouldMakeEntireScreenTouchable() {
+ // The touchable region is always the full area when expanded, whether we're showing the
+ // shade or the bouncer. It's also fully touchable when the screen off animation is playing
+ // since we don't want stray touches to go through the light reveal scrim to whatever is
+ // underneath.
+ return mIsStatusBarExpanded
+ || mCentralSurfaces.isBouncerShowing()
+ || mUnlockedScreenOffAnimationController.isAnimationPlaying();
+ }
+
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
new OnComputeInternalInsetsListener() {
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsStatusBarExpanded || mCentralSurfaces.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
+ if (shouldMakeEntireScreenTouchable()) {
return;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index e2790e4..a61cd23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -161,7 +161,18 @@
runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
- verify(controller).onLaunchAnimationCancelled()
+ verify(controller).onLaunchAnimationCancelled(false /* newKeyguardOccludedState */)
+ verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+ }
+
+ @Test
+ fun passesOccludedStateToLaunchAnimationCancelled_ifTrue() {
+ val runner = activityLaunchAnimator.createRunner(controller)
+ runner.onAnimationCancelled(true /* isKeyguardOccluded */)
+ runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+
+ waitForIdleSync()
+ verify(controller).onLaunchAnimationCancelled(true /* newKeyguardOccludedState */)
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
}
@@ -253,7 +264,7 @@
assertOnMainThread()
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
assertOnMainThread()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
deleted file mode 100644
index 419fedf..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2022 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.biometrics
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertNull
-import org.junit.Assert.assertTrue
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class BiometricMessageDeferralTest : SysuiTestCase() {
-
- @Test
- fun testProcessNoMessages_noDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf())
-
- assertNull(biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testProcessNonDeferredMessages_noDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
-
- // WHEN there are no deferred messages processed
- for (i in 0..3) {
- biometricMessageDeferral.processMessage(4, "test")
- }
-
- // THEN getDeferredMessage is null
- assertNull(biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testAllProcessedMessagesWereDeferred() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
-
- // WHEN all the processed messages are a deferred message
- for (i in 0..3) {
- biometricMessageDeferral.processMessage(1, "test")
- }
-
- // THEN deferredMessage will return the string associated with the deferred msgId
- assertEquals("test", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testReturnsMostFrequentDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
-
- // WHEN there's two msgId=1 processed and one msgId=2 processed
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(2, "msgId-2")
-
- // THEN the most frequent deferred message is that meets the threshold is returned
- assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testDeferredMessage_mustMeetThreshold() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
-
- // WHEN more nonDeferredMessages are shown than the deferred message
- val totalMessages = 10
- val nonDeferredMessagesCount =
- (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
- for (i in 0 until nonDeferredMessagesCount) {
- biometricMessageDeferral.processMessage(4, "non-deferred-msg")
- }
- for (i in nonDeferredMessagesCount until totalMessages) {
- biometricMessageDeferral.processMessage(1, "msgId-1")
- }
-
- // THEN there's no deferred message because it didn't meet the threshold
- assertNull(biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testDeferredMessage_manyExcludedMessages_getDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1))
-
- // WHEN more excludedMessages are shown than the deferred message
- val totalMessages = 10
- val excludedMessagesCount = (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
- for (i in 0 until excludedMessagesCount) {
- biometricMessageDeferral.processMessage(3, "excluded-msg")
- }
- for (i in excludedMessagesCount until totalMessages) {
- biometricMessageDeferral.processMessage(1, "msgId-1")
- }
-
- // THEN there IS a deferred message because the deferred msg meets the threshold amongst the
- // non-excluded messages
- assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testResetClearsOutCounts() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
-
- // GIVEN two msgId=1 events processed
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(1, "msgId-1")
-
- // WHEN counts are reset and then a single deferred message is processed (msgId=2)
- biometricMessageDeferral.reset()
- biometricMessageDeferral.processMessage(2, "msgId-2")
-
- // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
- assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testShouldDefer() {
- // GIVEN should defer msgIds 1 and 2
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1, 2))
-
- // THEN shouldDefer returns true for ids 1 & 2
- assertTrue(biometricMessageDeferral.shouldDefer(1))
- assertTrue(biometricMessageDeferral.shouldDefer(2))
-
- // THEN should defer returns false for ids 3 & 4
- assertFalse(biometricMessageDeferral.shouldDefer(3))
- assertFalse(biometricMessageDeferral.shouldDefer(4))
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
new file mode 100644
index 0000000..c9ccdb3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 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.biometrics
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.keyguard.logging.BiometricMessageDeferralLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FaceHelpMessageDeferralTest : SysuiTestCase() {
+ val threshold = .75f
+ @Mock lateinit var logger: BiometricMessageDeferralLogger
+ @Mock lateinit var dumpManager: DumpManager
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testProcessFrame_logs() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+ biometricMessageDeferral.processFrame(1)
+ verify(logger).logFrameProcessed(1, 1, "1")
+ }
+
+ @Test
+ fun testUpdateMessage_logs() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+ biometricMessageDeferral.updateMessage(1, "hi")
+ verify(logger).logUpdateMessage(1, "hi")
+ }
+
+ @Test
+ fun testReset_logs() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+ biometricMessageDeferral.reset()
+ verify(logger).reset()
+ }
+
+ @Test
+ fun testProcessNoMessages_noDeferredMessage() {
+ val biometricMessageDeferral = createMsgDeferral(emptySet())
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessNonDeferredMessages_noDeferredMessage() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // WHEN there are no deferred messages processed
+ for (i in 0..3) {
+ biometricMessageDeferral.processFrame(4)
+ biometricMessageDeferral.updateMessage(4, "test")
+ }
+
+ // THEN getDeferredMessage is null
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessMessagesWithDeferredMessage_deferredMessageWasNeverGivenAString() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ biometricMessageDeferral.processFrame(1)
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testAllProcessedMessagesWereDeferred() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+
+ // WHEN all the processed messages are a deferred message
+ for (i in 0..3) {
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "test")
+ }
+
+ // THEN deferredMessage will return the string associated with the deferred msgId
+ assertEquals("test", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testReturnsMostFrequentDeferredMessage() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // WHEN there's 80%of the messages are msgId=1 and 20% is msgId=2
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+
+ biometricMessageDeferral.processFrame(2)
+ biometricMessageDeferral.updateMessage(2, "msgId-2")
+
+ // THEN the most frequent deferred message is that meets the threshold is returned
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_mustMeetThreshold() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+
+ // WHEN more nonDeferredMessages are shown than the deferred message
+ val totalMessages = 10
+ val nonDeferredMessagesCount = (totalMessages * threshold).toInt() + 1
+ for (i in 0 until nonDeferredMessagesCount) {
+ biometricMessageDeferral.processFrame(4)
+ biometricMessageDeferral.updateMessage(4, "non-deferred-msg")
+ }
+ for (i in nonDeferredMessagesCount until totalMessages) {
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+ }
+
+ // THEN there's no deferred message because it didn't meet the threshold
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testResetClearsOutCounts() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // GIVEN two msgId=1 events processed
+ biometricMessageDeferral.processFrame(
+ 1,
+ )
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+
+ // WHEN counts are reset and then a single deferred message is processed (msgId=2)
+ biometricMessageDeferral.reset()
+ biometricMessageDeferral.processFrame(2)
+ biometricMessageDeferral.updateMessage(2, "msgId-2")
+
+ // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
+ assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testShouldDefer() {
+ // GIVEN should defer msgIds 1 and 2
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // THEN shouldDefer returns true for ids 1 & 2
+ assertTrue(biometricMessageDeferral.shouldDefer(1))
+ assertTrue(biometricMessageDeferral.shouldDefer(2))
+
+ // THEN should defer returns false for ids 3 & 4
+ assertFalse(biometricMessageDeferral.shouldDefer(3))
+ assertFalse(biometricMessageDeferral.shouldDefer(4))
+ }
+
+ private fun createMsgDeferral(messagesToDefer: Set<Int>): BiometricMessageDeferral {
+ return BiometricMessageDeferral(messagesToDefer, threshold, logger, dumpManager)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index eecbee5..a78c902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -27,11 +27,11 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor.forClass
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -60,7 +60,18 @@
@Mock
private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
- private lateinit var remoteAnimationTarget: RemoteAnimationTarget
+ private var surfaceControl1 = mock(SurfaceControl::class.java)
+ private var remoteTarget1 = RemoteAnimationTarget(
+ 0 /* taskId */, 0, surfaceControl1, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, surfaceControl1, Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+
+ private var surfaceControl2 = mock(SurfaceControl::class.java)
+ private var remoteTarget2 = RemoteAnimationTarget(
+ 1 /* taskId */, 0, surfaceControl2, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, surfaceControl2, Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+ private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
@Before
fun setUp() {
@@ -77,10 +88,7 @@
// All of these fields are final, so we can't mock them, but are needed so that the surface
// appear amount setter doesn't short circuit.
- remoteAnimationTarget = RemoteAnimationTarget(
- 0, 0, null, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
- mock(WindowConfiguration::class.java), false, mock(SurfaceControl::class.java), Rect(),
- mock(ActivityManager.RunningTaskInfo::class.java), false)
+ remoteAnimationTargets = arrayOf(remoteTarget1)
// Set the surface applier to our mock so that we can verify the arguments passed to it.
// This applier does not have any side effects within the unlock animation controller, so
@@ -99,7 +107,7 @@
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -130,7 +138,7 @@
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -154,7 +162,7 @@
`when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -176,7 +184,7 @@
`when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -196,7 +204,7 @@
@Test
fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -210,7 +218,7 @@
`when`(notificationShadeWindowController.isLaunchingActivity).thenReturn(true)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -225,11 +233,46 @@
keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
}
+
+ /**
+ * If we are not wake and unlocking, we expect the unlock animation to play normally.
+ */
+ @Test
+ fun surfaceAnimation_multipleTargets() {
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ arrayOf(remoteTarget1, remoteTarget2),
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ // Set appear to 50%, we'll just verify that we're not applying the identity matrix which
+ // means an animation is in progress.
+ keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
+
+ val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
+ verify(surfaceTransactionApplier, times(2)).scheduleApply(captor.capture())
+
+ val allParams = captor.allValues
+
+ val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2)
+ allParams.forEach { params ->
+ assertTrue(!params.matrix.isIdentity)
+ remainingTargets.remove(params.surface)
+ }
+
+ // Make sure we called applyParams with each of the surface controls once. The order does
+ // not matter, so don't explicitly check for that.
+ assertTrue(remainingTargets.isEmpty())
+
+ // Since the animation is running, we should not have finished the remote animation.
+ verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index ac8874b..945cf7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -19,8 +19,10 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
@@ -86,6 +88,7 @@
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -167,6 +170,8 @@
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
+ private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
+ @Mock
private ScreenLifecycle mScreenLifecycle;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@@ -259,7 +264,8 @@
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
- mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager);
+ mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
+ mFaceHelpMessageDeferral);
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -535,7 +541,7 @@
mController.setVisible(true);
mController.getKeyguardCallback().onBiometricHelp(
- KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
BiometricSourceType.FACE);
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
reset(mRotateTextViewController);
@@ -582,7 +588,7 @@
// WHEN there's a face not recognized message
mController.getKeyguardCallback().onBiometricHelp(
- KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
message,
BiometricSourceType.FACE);
@@ -748,8 +754,10 @@
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
0)).thenReturn(false);
- // WHEN help message received
+ // WHEN help message received and deferred message is valid
final String helpString = "helpMsg";
+ when(mFaceHelpMessageDeferral.getDeferredMessage()).thenReturn(helpString);
+ when(mFaceHelpMessageDeferral.shouldDefer(FACE_ACQUIRED_TOO_DARK)).thenReturn(true);
mKeyguardUpdateMonitorCallback.onBiometricHelp(
BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
helpString,
@@ -777,8 +785,10 @@
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
0)).thenReturn(true);
- // WHEN help message received
+ // WHEN help message received and deferredMessage is valid
final String helpString = "helpMsg";
+ when(mFaceHelpMessageDeferral.getDeferredMessage()).thenReturn(helpString);
+ when(mFaceHelpMessageDeferral.shouldDefer(FACE_ACQUIRED_TOO_DARK)).thenReturn(true);
mKeyguardUpdateMonitorCallback.onBiometricHelp(
BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
helpString,
@@ -1123,7 +1133,6 @@
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, pressToOpen);
}
-
@Test
public void coEx_faceSuccess_touchExplorationEnabled_showsFaceUnlockedSwipeToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y enabled
@@ -1269,6 +1278,87 @@
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
}
+ @Test
+ public void faceOnAcquired_processFrame() {
+ createController();
+
+ // WHEN face sends an acquired message
+ final int acquireInfo = 1;
+ mKeyguardUpdateMonitorCallback.onBiometricAcquired(BiometricSourceType.FACE, acquireInfo);
+
+ // THEN face help message deferral should process the acquired frame
+ verify(mFaceHelpMessageDeferral).processFrame(acquireInfo);
+ }
+
+ @Test
+ public void fingerprintOnAcquired_noProcessFrame() {
+ createController();
+
+ // WHEN fingerprint sends an acquired message
+ mKeyguardUpdateMonitorCallback.onBiometricAcquired(BiometricSourceType.FINGERPRINT, 1);
+
+ // THEN face help message deferral should NOT process any acquired frames
+ verify(mFaceHelpMessageDeferral, never()).processFrame(anyInt());
+ }
+
+ @Test
+ public void onBiometricHelp_fingerprint_faceHelpMessageDeferralDoesNothing() {
+ createController();
+
+ // WHEN fingerprint sends an onBiometricHelp
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ 1,
+ "placeholder",
+ BiometricSourceType.FINGERPRINT);
+
+ // THEN face help message deferral is NOT: reset, updated, or checked for shouldDefer
+ verify(mFaceHelpMessageDeferral, never()).reset();
+ verify(mFaceHelpMessageDeferral, never()).updateMessage(anyInt(), anyString());
+ verify(mFaceHelpMessageDeferral, never()).shouldDefer(anyInt());
+ }
+
+ @Test
+ public void onBiometricFailed_resetFaceHelpMessageDeferral() {
+ createController();
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ mKeyguardUpdateMonitorCallback.onBiometricAuthFailed(BiometricSourceType.FACE);
+
+ // THEN face help message deferral is reset
+ verify(mFaceHelpMessageDeferral).reset();
+ }
+
+ @Test
+ public void onBiometricError_resetFaceHelpMessageDeferral() {
+ createController();
+
+ // WHEN face has an error
+ mKeyguardUpdateMonitorCallback.onBiometricError(4, "string",
+ BiometricSourceType.FACE);
+
+ // THEN face help message deferral is reset
+ verify(mFaceHelpMessageDeferral).reset();
+ }
+
+ @Test
+ public void onBiometricHelp_faceAcquiredInfo_faceHelpMessageDeferral() {
+ createController();
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ final int msgId = 1;
+ final String helpString = "test";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ msgId,
+ "test",
+ BiometricSourceType.FACE);
+
+ // THEN face help message deferral is NOT reset and message IS updated
+ verify(mFaceHelpMessageDeferral, never()).reset();
+ verify(mFaceHelpMessageDeferral).updateMessage(msgId, helpString);
+ }
+
+
+
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index c3b4792..0915c21 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -657,6 +657,18 @@
final String packageName = versionedPackage.getPackageName();
final long versionCode = versionedPackage.getLongVersionCode();
+ if (mPm.mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+ mPm.mHandler.post(() -> {
+ try {
+ Slog.w(TAG, "Attempted to delete protected package: " + packageName);
+ observer.onPackageDeleted(packageName,
+ PackageManager.DELETE_FAILED_INTERNAL_ERROR, null);
+ } catch (RemoteException re) {
+ }
+ });
+ return;
+ }
+
try {
if (mPm.mInjector.getLocalService(ActivityTaskManagerInternal.class)
.isBaseOfLockedTask(packageName)) {