Merge "Avoid IPCs to check for checking enrollment status of face and fp." into tm-qpr-dev
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
index b1b432b..6fd2bf2 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
@@ -380,7 +380,7 @@
Tracer.trace();
Display display = getAutomatorBridge().getDefaultDisplay();
Point p = new Point();
- display.getSize(p);
+ display.getRealSize(p);
return p.x;
}
@@ -394,7 +394,7 @@
Tracer.trace();
Display display = getAutomatorBridge().getDefaultDisplay();
Point p = new Point();
- display.getSize(p);
+ display.getRealSize(p);
return p.y;
}
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 5e34c15..134a917 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -137,4 +137,13 @@
}
return false;
}
+
+ @Override
+ public boolean isConfigurationContext() {
+ Context context = mContext.get();
+ if (context != null) {
+ return context.isConfigurationContext();
+ }
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index b866723..b11ea29 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
@@ -108,6 +109,12 @@
}
break;
case Icon.TYPE_RESOURCE:
+ if (!(TextUtils.isEmpty(icon.getResPackage())
+ || context.getPackageName().equals(icon.getResPackage()))) {
+ // We can't properly resolve icons from other packages here, so fall back.
+ return icon.loadDrawable(context);
+ }
+
Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight);
if (result != null) {
return tintDrawable(icon, result);
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 2a625b027..25a1f68 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -86,7 +86,7 @@
optional int32 flags = 3;
}
-// Next id: 8
+// Next Tag: 9
message VibrationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 start_time = 1;
@@ -94,11 +94,43 @@
optional CombinedVibrationEffectProto effect = 3;
optional CombinedVibrationEffectProto original_effect = 4;
optional VibrationAttributesProto attributes = 5;
- optional int32 status = 6;
optional int64 duration_ms = 7;
+ optional Status status = 8;
+ reserved 6; // prev int32 status
+
+ // Also used by VibrationReported from frameworks/proto_logging/stats/atoms.proto.
+ // Next Tag: 26
+ enum Status {
+ UNKNOWN = 0;
+ RUNNING = 1;
+ FINISHED = 2;
+ FINISHED_UNEXPECTED = 3; // Didn't terminate in the usual way.
+ FORWARDED_TO_INPUT_DEVICES = 4;
+ CANCELLED_BINDER_DIED = 5;
+ CANCELLED_BY_SCREEN_OFF = 6;
+ CANCELLED_BY_SETTINGS_UPDATE = 7;
+ CANCELLED_BY_USER = 8;
+ CANCELLED_BY_UNKNOWN_REASON = 9;
+ CANCELLED_SUPERSEDED = 10;
+ IGNORED_ERROR_APP_OPS = 11;
+ IGNORED_ERROR_CANCELLING = 12;
+ IGNORED_ERROR_SCHEDULING = 13;
+ IGNORED_ERROR_TOKEN= 14;
+ IGNORED_APP_OPS = 15;
+ IGNORED_BACKGROUND = 16;
+ IGNORED_UNKNOWN_VIBRATION = 17;
+ IGNORED_UNSUPPORTED = 18;
+ IGNORED_FOR_EXTERNAL = 19;
+ IGNORED_FOR_HIGHER_IMPORTANCE = 20;
+ IGNORED_FOR_ONGOING = 21;
+ IGNORED_FOR_POWER = 22;
+ IGNORED_FOR_RINGER_MODE = 23;
+ IGNORED_FOR_SETTINGS = 24;
+ IGNORED_SUPERSEDED = 25;
+ }
}
-// Next id: 25
+// Next Tag: 25
message VibratorManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 vibrator_ids = 1;
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
index 58b8cc9..96860b0 100644
--- a/core/res/res/layout/side_fps_toast.xml
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -20,13 +20,14 @@
android:layout_height="wrap_content"
android:minWidth="350dp"
android:layout_gravity="center"
- android:theme="?attr/alertDialogTheme">
+ android:background="@color/side_fps_toast_background">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fp_power_button_enrollment_title"
android:singleLine="true"
android:ellipsize="end"
+ android:textColor="@color/side_fps_text_color"
android:paddingLeft="20dp"/>
<Space
android:layout_width="wrap_content"
@@ -39,5 +40,6 @@
android:text="@string/fp_power_button_enrollment_button_text"
android:paddingRight="20dp"
style="?android:attr/buttonBarNegativeButtonStyle"
+ android:textColor="@color/side_fps_button_color"
android:maxLines="1"/>
</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 33c9b95..ffaccd3 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -37,4 +37,10 @@
<color name="user_icon_6">#ff4ecde6</color><!-- cyan -->
<color name="user_icon_7">#fffbbc04</color><!-- yellow -->
<color name="user_icon_8">#fffa903e</color><!-- orange -->
+
+ <!-- Color for side fps toast dark theme-->
+ <color name="side_fps_toast_background">#2E3132</color>
+ <color name="side_fps_text_color">#EFF1F2</color>
+ <color name="side_fps_button_color">#33B9DB</color>
+
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 9fd894d..d5875f5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -447,4 +447,10 @@
<!-- Color of camera light when camera is in use -->
<color name="camera_privacy_light_day">#FFFFFF</color>
<color name="camera_privacy_light_night">#FFFFFF</color>
+
+ <!-- Color for side fps toast light theme -->
+ <color name="side_fps_toast_background">#F7F9FA</color>
+ <color name="side_fps_text_color">#191C1D</color>
+ <color name="side_fps_button_color">#00677E</color>
+
</resources>
diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
index c63d18b..0cee526 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
@@ -270,4 +270,13 @@
assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight);
}
+
+ @Test
+ public void resolveImage_iconWithOtherPackageResource_usesPackageContextDefinition()
+ throws IOException {
+ Icon icon = Icon.createWithResource("this_is_invalid", R.drawable.test32x24);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+ // This drawable must not be loaded - if it was, the code ignored the package specification.
+ assertThat(d).isNull();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index ff3c083..497a6f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -105,6 +105,10 @@
MATCH_PARENT));
((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
+ int orientation = getResources().getConfiguration().orientation;
+ setOrientation(orientation == Configuration.ORIENTATION_LANDSCAPE
+ ? LinearLayout.HORIZONTAL
+ : LinearLayout.VERTICAL);
updateContainerMargins(getResources().getConfiguration().orientation);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index 0e32663..7096a64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -111,9 +111,6 @@
private final TaskSnapshot mSnapshot;
private final Rect mSourceRectHint;
- private float mTaskSnapshotScaleX;
- private float mTaskSnapshotScaleY;
-
public PipSnapshotOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
mSnapshot = snapshot;
mSourceRectHint = new Rect(sourceRectHint);
@@ -125,16 +122,16 @@
@Override
public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
- mTaskSnapshotScaleX = (float) mSnapshot.getTaskSize().x
+ final float taskSnapshotScaleX = (float) mSnapshot.getTaskSize().x
/ mSnapshot.getHardwareBuffer().getWidth();
- mTaskSnapshotScaleY = (float) mSnapshot.getTaskSize().y
+ final float taskSnapshotScaleY = (float) mSnapshot.getTaskSize().y
/ mSnapshot.getHardwareBuffer().getHeight();
tx.show(mLeash);
tx.setLayer(mLeash, Integer.MAX_VALUE);
tx.setBuffer(mLeash, mSnapshot.getHardwareBuffer());
// Relocate the content to parentLeash's coordinates.
tx.setPosition(mLeash, -mSourceRectHint.left, -mSourceRectHint.top);
- tx.setScale(mLeash, mTaskSnapshotScaleX, mTaskSnapshotScaleY);
+ tx.setScale(mLeash, taskSnapshotScaleX, taskSnapshotScaleY);
tx.reparent(mLeash, parentLeash);
tx.apply();
}
@@ -146,20 +143,6 @@
@Override
public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
- // Work around to make sure the snapshot overlay is aligned with PiP window before
- // the atomicTx is committed along with the final WindowContainerTransaction.
- final SurfaceControl.Transaction nonAtomicTx = new SurfaceControl.Transaction();
- final float scaleX = (float) destinationBounds.width()
- / mSourceRectHint.width();
- final float scaleY = (float) destinationBounds.height()
- / mSourceRectHint.height();
- final float scale = Math.max(
- scaleX * mTaskSnapshotScaleX, scaleY * mTaskSnapshotScaleY);
- nonAtomicTx.setScale(mLeash, scale, scale);
- nonAtomicTx.setPosition(mLeash,
- -scale * mSourceRectHint.left / mTaskSnapshotScaleX,
- -scale * mSourceRectHint.top / mTaskSnapshotScaleY);
- nonAtomicTx.apply();
atomicTx.remove(mLeash);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index a0e2201..7619646 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -288,8 +288,10 @@
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this);
- mTargetViewContainer.show();
}
+ // always invoke show, since the target might still be VISIBLE while playing hide animation,
+ // so we want to ensure it will show back again
+ mTargetViewContainer.show();
}
/** Animates the magnetic dismiss target out and then sets it to GONE. */
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index 56d2967..f46de06 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -133,6 +133,11 @@
}
@Override
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ @Override
public void setIcon(Drawable icon) {
mIcon = icon;
if (mButton == null || icon == null) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 85f91fe..11cb9c1 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -264,12 +264,12 @@
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing Aid profile. -->
<string name="bluetooth_profile_hearing_aid">Hearing Aids</string>
- <!-- Bluetooth settings. The user-visible string that is used whenever referring to the LE_AUDIO profile. -->
- <string name="bluetooth_profile_le_audio">LE_AUDIO</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the LE audio profile. -->
+ <string name="bluetooth_profile_le_audio">LE audio</string>
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
<string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aids</string>
- <!-- Bluetooth settings. Connection options screen. The summary for the LE_AUDIO checkbox preference when LE_AUDIO is connected. -->
- <string name="bluetooth_le_audio_profile_summary_connected">Connected to LE_AUDIO</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the LE audio checkbox preference when LE audio is connected. -->
+ <string name="bluetooth_le_audio_profile_summary_connected">Connected to LE audio</string>
<!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. -->
<string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
index 625b214..d78f8e7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
@@ -63,6 +63,7 @@
final Button button = mPreference.getButton();
assertThat(button.getText().toString()).isEqualTo(testTitle);
+ assertThat(mPreference.getTitle().toString()).isEqualTo(testTitle);
}
@Test
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
deleted file mode 100644
index 5084ca4..0000000
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ /dev/null
@@ -1 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_3_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25" android:pivotX="40.25" android:pivotY="40.25" android:scaleX="0.975" android:scaleY="0.975"><path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#f2b8b5" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="9.950000000000003" android:translateY="10" android:pivotX="30" android:pivotY="30" android:scaleX="1.2" android:scaleY="1.2"><group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" android:translateY="38.75" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/></group><group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/></group></group><group android:name="_R_G_L_0_G" android:translateX="20.659" android:translateY="15.75"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "/><path android:name="_R_G_L_0_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "/><path android:name="_R_G_L_0_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_3_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="167" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="167" android:startOffset="0" android:valueFrom="2.5" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="83" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="0.975" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="167" android:valueFrom="0.975" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_2_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="83" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="83" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.543,0 0.299,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="417" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml
deleted file mode 100644
index c4f8181..0000000
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_unlock.xml
+++ /dev/null
@@ -1 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_3_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25" android:pivotX="40.25" android:pivotY="40.25" android:scaleX="0.975" android:scaleY="0.975"><path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#f2b8b5" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="9.950000000000003" android:translateY="10" android:pivotX="30" android:pivotY="30" android:scaleX="1.2" android:scaleY="1.2"><group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" android:translateY="38.75" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/></group><group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" android:scaleX="1" android:scaleY="1"><path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/></group></group><group android:name="_R_G_L_0_G" android:translateX="20.75" android:translateY="15.75" android:pivotX="19.341" android:pivotY="24.25" android:scaleX="0.5" android:scaleY="0"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 "/><path android:name="_R_G_L_0_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 "/><path android:name="_R_G_L_0_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_3_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="167" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="167" android:startOffset="0" android:valueFrom="2.5" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="83" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="0.975" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="167" android:valueFrom="0.975" android:valueTo="0" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="0" android:valueFrom="1" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="67" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="67" android:valueFrom="1.1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="233" android:startOffset="0" android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueTo="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="267" android:startOffset="233" android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueTo="M37.91 20.05 C37.91,20.05 37.89,14.16 37.89,14.16 C37.89,10.79 35.15,8.05 31.86,8.03 C28.46,8.01 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="167" android:startOffset="0" android:valueFrom="0.5" android:valueTo="0.5" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="167" android:startOffset="0" android:valueFrom="0.5" android:valueTo="0.5" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="167" android:valueFrom="0.5" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="167" android:valueFrom="0.5" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.05,1.4 0.1,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="167" android:valueFrom="0" android:valueTo="0.5" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="683" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
deleted file mode 100644
index c05a8d5..0000000
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ /dev/null
@@ -1 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_3_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25" android:pivotX="40.25" android:pivotY="40.25" android:scaleX="0.975" android:scaleY="0"><path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#f2b8b5" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0" android:strokeAlpha="0" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="9.950000000000003" android:translateY="10" android:pivotX="30" android:pivotY="30" android:scaleX="1.2" android:scaleY="1.2"><group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30" android:translateY="38.75" android:scaleX="0" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/></group><group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30" android:translateY="25" android:pivotX="0.002" android:pivotY="7.488" android:scaleX="1" android:scaleY="0"><path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#f2b8b5" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/></group></group><group android:name="_R_G_L_0_G" android:translateX="20.659" android:translateY="15.75"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "/><path android:name="_R_G_L_0_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "/><path android:name="_R_G_L_0_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="1" android:trimPathOffset="0" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_3_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="83" android:startOffset="0" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeWidth" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeWidth" android:duration="233" android:startOffset="67" android:valueFrom="0" android:valueTo="2.5" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="83" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_2_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="0.975" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="167" android:valueFrom="0" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="167" android:valueFrom="0" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="267" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="267" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="167" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="100" android:startOffset="167" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="100" android:startOffset="167" android:valueFrom="0" android:valueTo="1.1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="67" android:startOffset="267" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="67" android:startOffset="267" android:valueFrom="1.1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_2_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="167" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="350" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml
deleted file mode 100644
index 1694429..0000000
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_unlock.xml
+++ /dev/null
@@ -1 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="80dp" android:width="80dp" android:viewportHeight="80" android:viewportWidth="80"><group android:name="_R_G"><group android:name="_R_G_L_2_G" android:translateX="-0.25" android:translateY="-0.25"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#474747" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.21 0.25 C18.13,0.25 0.25,18.17 0.25,40.25 C0.25,62.33 18.13,80.25 40.21,80.25 C62.33,80.25 80.25,62.33 80.25,40.25 C80.25,18.17 62.33,0.25 40.21,0.25c "/></group><group android:name="_R_G_L_1_G" android:translateX="20.75" android:translateY="15.75"><path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 "/><path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 "/><path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 "/><path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 "/></group><group android:name="_R_G_L_0_G" android:translateX="37.357" android:translateY="43.25" android:pivotX="2.75" android:pivotY="2.75" android:scaleX="1.41866" android:scaleY="1.41866"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#d3e3fd" android:fillAlpha="0" android:fillType="nonZero" android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="107" android:startOffset="0" android:valueFrom="M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " android:valueTo="M30.81 32.26 C30.56,32.49 30.27,38.76 29.96,38.9 C29.77,39.46 29.13,39.94 28.57,40.26 C28.15,40.51 26.93,40.65 26.4,40.65 C26.18,40.65 11.91,40.62 11.71,40.58 C10.68,40.53 9.06,39.79 8.89,38.88 C8.6,38.74 8.34,32.48 8.1,32.27 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="143" android:startOffset="107" android:valueFrom="M30.81 32.26 C30.56,32.49 30.27,38.76 29.96,38.9 C29.77,39.46 29.13,39.94 28.57,40.26 C28.15,40.51 26.93,40.65 26.4,40.65 C26.18,40.65 11.91,40.62 11.71,40.58 C10.68,40.53 9.06,39.79 8.89,38.88 C8.6,38.74 8.34,32.48 8.1,32.27 " android:valueTo="M30.64 30.14 C30.64,30.14 30.64,38.14 30.64,38.14 C30.64,38.77 30.36,39.32 29.91,39.69 C29.57,39.97 29.12,40.14 28.64,40.14 C28.64,40.14 10.14,40.14 10.14,40.14 C9.04,40.14 8.14,39.25 8.14,38.14 C8.14,38.14 8.14,30.14 8.14,30.14 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0.331,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="strokeAlpha" android:duration="140" android:startOffset="0" android:valueFrom="1" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="strokeAlpha" android:duration="50" android:startOffset="140" android:valueFrom="1" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="107" android:startOffset="0" android:valueFrom="M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " android:valueTo="M18.93 32.18 C17.11,32.68 16.62,30.26 16.62,30.26 C16.62,28.78 17.81,27.59 19.39,27.59 C20.96,27.59 22.15,28.78 22.15,30.26 C22.15,30.26 22.15,30.34 22.15,30.34 C22.15,30.89 21.11,32.54 19.57,32.19 C19.19,32.1 20.48,31.09 20.34,30.71 C20.34,30.71 20.02,29.88 20.02,29.88 C19.88,29.5 19.53,29.25 19.15,29.25 C18.63,29.25 18,29.67 18,30.22 C18,30.57 18.06,31.08 18.32,31.51 C18.49,31.8 19.02,32.25 19.79,32.04 C20.41,31.7 20.38,31.36 20.38,31.36 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="107" android:startOffset="107" android:valueFrom="M18.93 32.18 C17.11,32.68 16.62,30.26 16.62,30.26 C16.62,28.78 17.81,27.59 19.39,27.59 C20.96,27.59 22.15,28.78 22.15,30.26 C22.15,30.26 22.15,30.34 22.15,30.34 C22.15,30.89 21.11,32.54 19.57,32.19 C19.19,32.1 20.48,31.09 20.34,30.71 C20.34,30.71 20.02,29.88 20.02,29.88 C19.88,29.5 19.53,29.25 19.15,29.25 C18.63,29.25 18,29.67 18,30.22 C18,30.57 18.06,31.08 18.32,31.51 C18.49,31.8 19.02,32.25 19.79,32.04 C20.41,31.7 20.38,31.36 20.38,31.36 " android:valueTo="M19.42 31.53 C18.15,31.52 18.11,30.33 18.11,30.33 C18.11,29.59 18.66,28.98 19.4,28.98 C20.13,28.98 20.69,29.59 20.69,30.33 C20.69,30.33 20.69,30.37 20.69,30.37 C20.69,30.64 20.49,30.87 20.25,30.87 C20.07,30.87 19.91,30.74 19.84,30.55 C19.84,30.55 19.69,30.14 19.69,30.14 C19.63,29.94 19.46,29.82 19.28,29.82 C19.04,29.82 18.61,30.02 18.61,30.29 C18.61,30.43 18.6,30.75 18.76,31.03 C18.87,31.21 19.21,31.77 19.96,31.41 C20.69,31.01 20.69,30.34 20.69,30.34 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_2_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="250" android:startOffset="0" android:valueFrom="M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " android:valueTo="M8.14 30.22 C8.14,30.22 8.14,22.22 8.14,22.22 C8.14,21.71 8.33,21.25 8.64,20.9 C9,20.48 9.54,20.22 10.14,20.22 C10.14,20.22 28.64,20.22 28.64,20.22 C29.75,20.22 30.64,21.11 30.64,22.22 C30.64,22.22 30.64,30.14 30.64,30.14 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.189,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_3_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="pathData" android:duration="95" android:startOffset="0" android:valueFrom="M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " android:valueTo="M11.47 14.84 C11.47,14.84 12.21,11.43 13.54,9.84 C14.84,8.28 16.68,7.22 19.35,7.22 C22.01,7.22 23.98,8.4 25.19,10.18 C26.39,11.96 27.25,14.84 27.25,14.84 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.541,0 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="24" android:startOffset="95" android:valueFrom="M11.47 14.84 C11.47,14.84 12.21,11.43 13.54,9.84 C14.84,8.28 16.68,7.22 19.35,7.22 C22.01,7.22 23.98,8.4 25.19,10.18 C26.39,11.96 27.25,14.84 27.25,14.84 " android:valueTo="M12.11 16.85 C12.11,16.85 12.82,12.71 13.37,11.5 C14.17,9.24 16.38,7.53 19.35,7.53 C22.32,7.53 24.61,9.32 25.35,11.72 C25.61,12.64 26.62,16.85 26.62,16.85 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0.833,0.767 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="81" android:startOffset="119" android:valueFrom="M12.11 16.85 C12.11,16.85 12.82,12.71 13.37,11.5 C14.17,9.24 16.38,7.53 19.35,7.53 C22.32,7.53 24.61,9.32 25.35,11.72 C25.61,12.64 26.62,16.85 26.62,16.85 " android:valueTo="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.233 0.261,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="pathData" android:duration="233" android:startOffset="200" android:valueFrom="M13.12 20.04 C13.12,20.04 13.11,14.15 13.11,14.15 C13.11,10.77 15.91,8.04 19.36,8.04 C22.81,8.04 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueTo="M37.91 20.05 C37.91,20.05 37.89,14.16 37.89,14.16 C37.89,10.79 35.15,8.05 31.86,8.03 C28.46,8.01 25.61,10.77 25.61,14.15 C25.61,14.15 25.62,20.04 25.62,20.04 " android:valueType="pathType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.123,0 0.23,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="fillAlpha" android:duration="120" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="fillAlpha" android:duration="20" android:startOffset="120" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleX" android:duration="120" android:startOffset="0" android:valueFrom="1.4186600000000003" android:valueTo="1.4186600000000003" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="120" android:startOffset="0" android:valueFrom="1.4186600000000003" android:valueTo="1.4186600000000003" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleX" android:duration="130" android:startOffset="120" android:valueFrom="1.4186600000000003" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="scaleY" android:duration="130" android:startOffset="120" android:valueFrom="1.4186600000000003" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.001,0 0.43,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="517" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index e1b294f..d633803 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -49,11 +49,11 @@
<FrameLayout
android:id="@+id/biometric_icon_frame"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
- <ImageView
+ <com.airbnb.lottie.LottieAnimationView
android:id="@+id/biometric_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml b/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml
index ce53e27..01ea31f 100644
--- a/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_fingerprint_view.xml
@@ -17,7 +17,7 @@
<com.android.systemui.biometrics.AuthBiometricFingerprintView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contents"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_fingerprint_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_fingerprint_lottie.json
new file mode 100644
index 0000000..cc68a83
--- /dev/null
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_fingerprint_lottie.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":25,"w":80,"h":80,"nm":"error_to_fingerprint","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.299],"y":[1]},"o":{"x":[0.543],"y":[0]},"t":5,"s":[0]},{"t":20,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.95,40,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.2,-7.5],[1.2,-7.5],[1.2,7.5],[-1.2,7.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.002,32.488],"ix":2},"a":{"a":0,"k":[0.002,7.488],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.659,0.6],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.6,0.92],"y":[1,1.096]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":4,"s":[100,110]},{"t":10,"s":[100,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top!","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.2,-1.25],[1.2,-1.25],[1.2,1.25],[-1.2,1.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30,38.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.6,0.6],"y":[1,1]},"o":{"x":[0.853,0.853],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.92,0.92],"y":[1.06,1.06]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":4,"s":[110,110]},{"t":10,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom!","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":25,"st":-30,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[97.5,97.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[2.5]},{"t":10,"s":[0]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":10,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"t":14,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_unlock_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_unlock_lottie.json
new file mode 100644
index 0000000..aaf7e58
--- /dev/null
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_error_to_unlock_lottie.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":41,"w":80,"h":80,"nm":"error_to_unlock","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.091,40,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[1.4,1.4,0]},"t":10,"s":[50,50,100]},{"t":30,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":14,"s":[{"i":[[0,0],[0,0],[-3.452,0],[0,-3.375],[0,0]],"o":[[0,0],[0,-3.375],[3.452,0],[0,0],[0,0]],"v":[[-6.217,12.558],[-6.234,6.669],[0.016,0.558],[6.266,6.669],[6.283,12.558]],"c":false}]},{"t":30,"s":[{"i":[[0,0],[0,0],[3.292,0.021],[0,-3.375],[0,0]],"o":[[0,0],[0,-3.375],[-3.393,-0.022],[0,0],[0,0]],"v":[[18.568,12.573],[18.552,6.684],[12.516,0.553],[6.266,6.669],[6.283,12.558]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.307,0.352],[-0.601,0],[0,0],[0,-1.104],[0,0]],"o":[[0,0],[0,-0.503],[0.367,-0.42],[0,0],[1.104,0],[0,0],[0,0]],"v":[[-11.2,14.15],[-11.198,6.146],[-10.705,4.831],[-9.198,4.146],[9.302,4.146],[11.302,6.146],[11.3,14.07]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.736,0],[0,-0.741],[0,0],[0.243,0],[0.066,0.191],[0,0],[0.179,0],[0,-0.276],[-0.162,-0.273],[-0.755,0.357],[0,0]],"o":[[-1.273,-0.008],[0,-0.741],[0.736,0],[0,0],[0,0.276],[-0.181,0],[0,0],[-0.066,-0.191],[-0.243,0],[-0.002,0.139],[0.109,0.182],[0.727,-0.402],[0,0]],"v":[[0.082,3.187],[-1.235,1.986],[0.055,0.642],[1.346,1.986],[1.346,2.026],[0.905,2.527],[0.498,2.212],[0.35,1.794],[-0.057,1.479],[-0.733,1.951],[-0.58,2.686],[0.619,3.071],[1.351,2]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.446,-0.367],[0.481,0],[0,0],[0,1.104],[0,0]],"o":[[0,0],[0,0.623],[-0.345,0.284],[0,0],[-1.104,0],[0,0],[0,0]],"v":[[11.302,-10.469],[11.302,-2.469],[10.57,-0.923],[9.302,-0.469],[-9.198,-0.469],[-11.198,-2.469],[-11.198,-10.469]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.95,40,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.2,-7.5],[1.2,-7.5],[1.2,7.5],[-1.2,7.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.002,32.488],"ix":2},"a":{"a":0,"k":[0.002,7.488],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.659,0.6],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.6,0.92],"y":[1,1.096]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":4,"s":[100,110]},{"t":10,"s":[100,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top!","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.2,-1.25],[1.2,-1.25],[1.2,1.25],[-1.2,1.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30,38.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.6,0.6],"y":[1,1]},"o":{"x":[0.853,0.853],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.92,0.92],"y":[1.06,1.06]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":4,"s":[110,110]},{"t":10,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom!","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":86,"st":-30,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[97.5,97.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[2.5]},{"t":10,"s":[0]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":10,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"t":14,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_error_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_error_lottie.json
new file mode 100644
index 0000000..78bccba
--- /dev/null
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_error_lottie.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":80,"h":80,"nm":"fingerprint_to_error","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[0]},{"t":10,"s":[100]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.95,40,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[120,120,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.2,-7.5],[1.2,-7.5],[1.2,7.5],[-1.2,7.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019610882,0.721568644047,0.709803938866,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.002,32.488],"ix":2},"a":{"a":0,"k":[0.002,7.488],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.08],"y":[0,0.096]},"t":10,"s":[100,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.341,0.4],"y":[0,0]},"t":16,"s":[100,110]},{"t":20,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top!","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.2,-1.25],[1.2,-1.25],[1.2,1.25],[-1.2,1.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.949019610882,0.721568644047,0.709803938866,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30,38.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.08,0.08],"y":[0.06,0.06]},"t":10,"s":[0,0]},{"i":{"x":[0.147,0.147],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":16,"s":[110,110]},{"t":20,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom!","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":21,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".red200","cl":"red200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"t":9,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[97.5,97.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"t":9,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.949019607843,0.721568627451,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"t":9,"s":[100]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4,"s":[0]},{"t":18,"s":[2.5]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":4,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_unlock_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_unlock_lottie.json
new file mode 100644
index 0000000..313c6c5
--- /dev/null
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_fingerprint_to_unlock_lottie.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":31,"w":80,"h":80,"nm":"fingerprint_to_unlock","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.107,46,0],"ix":2,"l":2},"a":{"a":0,"k":[2.75,2.75,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.43,0.43,0.2],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"t":7.199,"s":[141.866,141.866,100]},{"t":15,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.199,"s":[0]},{"t":8.400390625,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[2.75,2.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.091,40,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.767},"o":{"x":0.541,"y":0},"t":0,"s":[{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false}]},{"i":{"x":0.833,"y":0.767},"o":{"x":0.167,"y":0.233},"t":5.715,"s":[{"i":[[0,0],[-1.323,1.591],[-2.674,0],[-1.207,-1.781],[0,0]],"o":[[0,0],[1.298,-1.562],[2.657,0],[1.206,1.781],[0,0]],"v":[[-7.87,7.358],[-5.804,2.36],[0.009,-0.261],[5.845,2.706],[7.905,7.358]],"c":false}]},{"i":{"x":0.261,"y":1},"o":{"x":0.167,"y":0.233},"t":7.143,"s":[{"i":[[0,0],[-0.549,1.21],[-2.975,0],[-0.74,-2.398],[0,0]],"o":[[0,0],[0.796,-2.263],[2.964,0],[0.258,0.927],[0,0]],"v":[[-7.231,9.37],[-5.97,4.027],[0.012,0.056],[6.008,4.239],[7.277,9.37]],"c":false}]},{"i":{"x":0.23,"y":1},"o":{"x":0.123,"y":0},"t":12,"s":[{"i":[[0,0],[0,0],[-3.452,0],[0,-3.375],[0,0]],"o":[[0,0],[0,-3.375],[3.452,0],[0,0],[0,0]],"v":[[-6.217,12.558],[-6.234,6.669],[0.016,0.558],[6.266,6.669],[6.283,12.558]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[0,0],[3.292,0.021],[0,-3.375],[0,0]],"o":[[0,0],[0,-3.375],[-3.393,-0.022],[0,0],[0,0]],"v":[[18.568,12.573],[18.552,6.684],[12.516,0.553],[6.266,6.669],[6.283,12.558]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.189,"y":1},"o":{"x":0.541,"y":0},"t":0,"s":[{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false}]},{"t":15,"s":[{"i":[[0,0],[0,0],[-0.307,0.352],[-0.601,0],[0,0],[0,-1.104],[0,0]],"o":[[0,0],[0,-0.503],[0.367,-0.42],[0,0],[1.104,0],[0,0],[0,0]],"v":[[-11.2,14.15],[-11.198,6.146],[-10.705,4.831],[-9.198,4.146],[9.302,4.146],[11.302,6.146],[11.3,14.07]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.767},"o":{"x":0.541,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.233},"t":6.428,"s":[{"i":[[0,0],[0,0],[-1.576,0],[0,-1.474],[0,0],[1.541,0.347],[0.142,0.379],[0,0],[0.383,0],[0,-0.549],[-0.256,-0.431],[-0.768,0.207],[0,0]],"o":[[-1.823,0.497],[0,-1.474],[1.576,0],[0,0],[0,0.549],[-0.378,-0.085],[0,0],[-0.142,-0.379],[-0.521,0],[-0.002,0.353],[0.171,0.288],[0.622,-0.344],[0,0]],"v":[[-0.41,3.841],[-2.717,1.917],[0.047,-0.756],[2.811,1.917],[2.811,1.996],[0.225,3.848],[0.995,2.366],[0.679,1.534],[-0.193,0.909],[-1.338,1.879],[-1.026,3.169],[0.445,3.702],[1.036,3.015]],"c":false}]},{"t":12.857421875,"s":[{"i":[[0,0],[0,0],[-0.736,0],[0,-0.741],[0,0],[0.243,0],[0.066,0.191],[0,0],[0.179,0],[0,-0.276],[-0.162,-0.273],[-0.755,0.357],[0,0]],"o":[[-1.273,-0.008],[0,-0.741],[0.736,0],[0,0],[0,0.276],[-0.181,0],[0,0],[-0.066,-0.191],[-0.243,0],[-0.002,0.139],[0.109,0.182],[0.727,-0.402],[0,0]],"v":[[0.082,3.187],[-1.235,1.986],[0.055,0.642],[1.346,1.986],[1.346,2.026],[0.905,2.527],[0.498,2.212],[0.35,1.794],[-0.057,1.479],[-0.733,1.951],[-0.58,2.686],[0.619,3.071],[1.351,2]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":8.4,"s":[100]},{"t":11.3984375,"s":[0]}],"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.767},"o":{"x":0.541,"y":0},"t":0,"s":[{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false}]},{"i":{"x":0.331,"y":1},"o":{"x":0.167,"y":0.233},"t":6.428,"s":[{"i":[[0,0],[0.313,-0.134],[0.554,-0.317],[0.535,0],[0.203,0.046],[0.175,0.919],[0.232,0.216]],"o":[[-0.249,0.232],[-0.196,0.557],[-0.424,0.245],[-0.216,0],[-1.03,-0.044],[-0.288,-0.132],[0,0]],"v":[[11.468,-8.353],[10.62,-1.716],[9.232,-0.353],[7.057,0.034],[-7.634,-0.037],[-10.453,-1.739],[-11.238,-8.347]],"c":false}]},{"t":15,"s":[{"i":[[0,0],[0,0],[0.446,-0.367],[0.481,0],[0,0],[0,1.104],[0,0]],"o":[[0,0],[0,0.623],[-0.345,0.284],[0,0],[-1.104,0],[0,0],[0,0]],"v":[[11.302,-10.469],[11.302,-2.469],[10.57,-0.923],[9.302,-0.469],[-9.198,-0.469],[-11.198,-2.469],[-11.198,-10.469]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450980392,0.890196078431,0.992156862745,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[{"tm":30,"cm":"1","dr":0},{"tm":51,"cm":"350ms\r","dr":0},{"tm":69,"cm":"650ms\r","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8ea2c0c..f344721 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -937,7 +937,8 @@
<!-- Biometric Dialog values -->
<dimen name="biometric_dialog_face_icon_size">64dp</dimen>
- <dimen name="biometric_dialog_fingerprint_icon_size">80dp</dimen>
+ <dimen name="biometric_dialog_fingerprint_icon_width">80dp</dimen>
+ <dimen name="biometric_dialog_fingerprint_icon_height">80dp</dimen>
<dimen name="biometric_dialog_button_negative_max_width">160dp</dimen>
<dimen name="biometric_dialog_button_positive_max_width">136dp</dimen>
<dimen name="biometric_dialog_corner_size">4dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
index 55611f7..e60d4e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.Log
-import android.widget.ImageView
+import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
@@ -33,8 +33,8 @@
/** Face only icon animator for BiometricPrompt. */
class AuthBiometricFaceIconController(
- context: Context,
- iconView: ImageView
+ context: Context,
+ iconView: LottieAnimationView
) : AuthIconController(context, iconView) {
// false = dark to light, true = light to dark
@@ -76,44 +76,44 @@
if (newState == STATE_AUTHENTICATING_ANIMATING_IN) {
showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticating
+ R.string.biometric_dialog_face_icon_description_authenticating
)
} else if (newState == STATE_AUTHENTICATING) {
startPulsing()
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticating
+ R.string.biometric_dialog_face_icon_description_authenticating
)
} else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_confirmed
+ R.string.biometric_dialog_face_icon_description_confirmed
)
} else if (lastStateIsErrorIcon && newState == STATE_IDLE) {
animateIconOnce(R.drawable.face_dialog_error_to_idle)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_idle
+ R.string.biometric_dialog_face_icon_description_idle
)
} else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) {
animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticated
+ R.string.biometric_dialog_face_icon_description_authenticated
)
} else if (newState == STATE_ERROR && oldState != STATE_ERROR) {
animateIconOnce(R.drawable.face_dialog_dark_to_error)
} else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticated
+ R.string.biometric_dialog_face_icon_description_authenticated
)
} else if (newState == STATE_PENDING_CONFIRMATION) {
animateIconOnce(R.drawable.face_dialog_wink_from_dark)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticated
+ R.string.biometric_dialog_face_icon_description_authenticated
)
} else if (newState == STATE_IDLE) {
showStaticDrawable(R.drawable.face_dialog_idle_static)
iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_idle
+ R.string.biometric_dialog_face_icon_description_idle
)
} else {
Log.w(TAG, "Unhandled state: $newState")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
index 3e4e573..40d1eff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -16,42 +16,43 @@
package com.android.systemui.biometrics
+import android.annotation.RawRes
import android.content.Context
-import android.graphics.drawable.Drawable
-import android.widget.ImageView
+import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
-import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR
import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
+import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
/** Face/Fingerprint combined icon animator for BiometricPrompt. */
class AuthBiometricFingerprintAndFaceIconController(
- context: Context,
- iconView: ImageView
+ context: Context,
+ iconView: LottieAnimationView
) : AuthBiometricFingerprintIconController(context, iconView) {
override val actsAsConfirmButton: Boolean = true
override fun shouldAnimateForTransition(
- @BiometricState oldState: Int,
- @BiometricState newState: Int
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
): Boolean = when (newState) {
STATE_PENDING_CONFIRMATION -> true
STATE_AUTHENTICATED -> false
else -> super.shouldAnimateForTransition(oldState, newState)
}
+ @RawRes
override fun getAnimationForTransition(
- @BiometricState oldState: Int,
- @BiometricState newState: Int
- ): Drawable? = when (newState) {
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ): Int? = when (newState) {
STATE_PENDING_CONFIRMATION -> {
if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- context.getDrawable(R.drawable.fingerprint_dialog_error_to_unlock)
+ R.raw.fingerprint_dialogue_error_to_unlock_lottie
} else {
- context.getDrawable(R.drawable.fingerprint_dialog_fp_to_unlock)
+ R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie
}
}
STATE_AUTHENTICATED -> null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 606a73a..589ec0e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -16,10 +16,9 @@
package com.android.systemui.biometrics
+import android.annotation.RawRes
import android.content.Context
-import android.graphics.drawable.AnimatedVectorDrawable
-import android.graphics.drawable.Drawable
-import android.widget.ImageView
+import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
@@ -32,42 +31,42 @@
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
- context: Context,
- iconView: ImageView
+ context: Context,
+ iconView: LottieAnimationView
) : AuthIconController(context, iconView) {
- var iconLayoutParamsSize = 0
+ var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
return
}
- iconView.layoutParams.width = value
- iconView.layoutParams.height = value
+ iconView.layoutParams.width = value.first
+ iconView.layoutParams.height = value.second
field = value
}
init {
- iconLayoutParamsSize = context.resources.getDimensionPixelSize(
- R.dimen.biometric_dialog_fingerprint_icon_size
- )
+ iconLayoutParamSize = Pair(context.resources.getDimensionPixelSize(
+ R.dimen.biometric_dialog_fingerprint_icon_width),
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_dialog_fingerprint_icon_height))
}
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
val icon = getAnimationForTransition(lastState, newState) ?: return
- iconView.setImageDrawable(icon)
+ if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
+ iconView.setAnimation(icon)
+ }
val iconContentDescription = getIconContentDescription(newState)
if (iconContentDescription != null) {
iconView.contentDescription = iconContentDescription
}
- (icon as? AnimatedVectorDrawable)?.apply {
- reset()
- if (shouldAnimateForTransition(lastState, newState)) {
- forceAnimationOnUI()
- start()
- }
+ iconView.frame = 0
+ if (shouldAnimateForTransition(lastState, newState)) {
+ iconView.playAnimation()
}
}
@@ -86,8 +85,8 @@
}
protected open fun shouldAnimateForTransition(
- @BiometricState oldState: Int,
- @BiometricState newState: Int
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
) = when (newState) {
STATE_HELP,
STATE_ERROR -> true
@@ -97,24 +96,27 @@
else -> false
}
+ @RawRes
protected open fun getAnimationForTransition(
- @BiometricState oldState: Int,
- @BiometricState newState: Int
- ): Drawable? {
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int
+ ): Int? {
val id = when (newState) {
STATE_HELP,
- STATE_ERROR -> R.drawable.fingerprint_dialog_fp_to_error
+ STATE_ERROR -> {
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+ }
STATE_AUTHENTICATING_ANIMATING_IN,
STATE_AUTHENTICATING -> {
if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- R.drawable.fingerprint_dialog_error_to_fp
+ R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
} else {
- R.drawable.fingerprint_dialog_fp_to_error
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
}
}
- STATE_AUTHENTICATED -> R.drawable.fingerprint_dialog_fp_to_error
+ STATE_AUTHENTICATED -> R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
else -> return null
}
- return if (id != null) context.getDrawable(id) else null
+ return if (id != null) return id else null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
index 24046f0..31baa0f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
@@ -90,8 +90,9 @@
fun updateOverrideIconLayoutParamsSize() {
udfpsAdapter?.let {
- (mIconController as? AuthBiometricFingerprintIconController)?.iconLayoutParamsSize =
- it.getSensorDiameter(scaleFactorProvider?.provide() ?: 1.0f)
+ val sensorDiameter = it.getSensorDiameter(scaleFactorProvider?.provide() ?: 1.0f)
+ (mIconController as? AuthBiometricFingerprintIconController)?.iconLayoutParamSize =
+ Pair(sensorDiameter, sensorDiameter)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
index ce5e600..15f487b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
@@ -22,15 +22,15 @@
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import android.util.Log
-import android.widget.ImageView
+import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
private const val TAG = "AuthIconController"
/** Controller for animating the BiometricPrompt icon/affordance. */
abstract class AuthIconController(
- protected val context: Context,
- protected val iconView: ImageView
+ protected val context: Context,
+ protected val iconView: LottieAnimationView
) : Animatable2.AnimationCallback() {
/** If this controller should ignore events and pause. */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index d7ae9ef..e866b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -41,14 +41,14 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
-import com.android.systemui.util.LargeScreenUtils;
+
+import com.airbnb.lottie.LottieAnimationView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -133,7 +133,7 @@
private TextView mSubtitleView;
private TextView mDescriptionView;
private View mIconHolderView;
- protected ImageView mIconView;
+ protected LottieAnimationView mIconView;
protected TextView mIndicatorView;
@VisibleForTesting @NonNull AuthIconController mIconController;
@@ -824,25 +824,12 @@
return new AuthDialog.LayoutParams(width, totalHeight);
}
- private boolean isLargeDisplay() {
- return LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
- final boolean isLargeDisplay = isLargeDisplay();
-
- final int newWidth;
- if (isLargeDisplay) {
- // TODO(b/201811580): Unless we can come up with a one-size-fits-all equation, we may
- // want to consider moving this to an overlay.
- newWidth = 2 * Math.min(width, height) / 3;
- } else {
- newWidth = Math.min(width, height);
- }
+ final int newWidth = Math.min(width, height);
// Use "newWidth" instead, so the landscape dialog width is the same as the portrait
// width.
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 8757904..00b0ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -68,7 +68,7 @@
.addFeature("feature")
val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
if (useAppIcon) {
- routeInfo.setPackageName(TEST_PACKAGE_NAME)
+ routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
}
statusBarManager.updateMediaTapToTransferSenderDisplay(
@@ -134,7 +134,7 @@
.addFeature("feature")
val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false")
if (useAppIcon) {
- routeInfo.setPackageName(TEST_PACKAGE_NAME)
+ routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
}
statusBarManager.updateMediaTapToTransferReceiverDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 0f1ae00..196ea22 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -143,7 +143,7 @@
super.updateChipView(newChipInfo, currentChipView)
setIcon(
currentChipView,
- newChipInfo.routeInfo.packageName,
+ newChipInfo.routeInfo.clientPackageName,
newChipInfo.appIconDrawableOverride,
newChipInfo.appNameOverride
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index b94b8bf..92d9ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -122,7 +122,7 @@
val chipState = newChipInfo.state
// App icon
- val iconName = setIcon(currentChipView, newChipInfo.routeInfo.packageName)
+ val iconName = setIcon(currentChipView, newChipInfo.routeInfo.clientPackageName)
// Text
val otherDeviceName = newChipInfo.routeInfo.name.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 2b782b6..3f4fd50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -165,7 +165,7 @@
}
private void positiveFeedback(View v) {
- mGutsContainer.closeControls(v, false);
+ mGutsContainer.closeControls(v, /* save= */ false);
handleFeedback(true);
}
@@ -176,7 +176,7 @@
menuItem = mMenuRowPlugin.getLongpressMenuItem(mContext);
}
- mGutsContainer.closeControls(v, false);
+ mGutsContainer.closeControls(v, /* save= */ false);
mNotificationGutsManager.openGuts(mExpandableNotificationRow, 0, 0, menuItem);
handleFeedback(false);
}
@@ -203,7 +203,7 @@
}
private void closeControls(View v) {
- mGutsContainer.closeControls(v, false);
+ mGutsContainer.closeControls(v, /* save= */ false);
}
@Override
@@ -232,7 +232,7 @@
}
@Override
- public boolean shouldBeSaved() {
+ public boolean shouldBeSavedOnClose() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 7120fe5..0ce9656 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -157,7 +157,7 @@
mShadeController.animateCollapsePanels();
mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle());
}
- mGutsContainer.closeControls(v, true);
+ mGutsContainer.closeControls(v, /* save= */ true);
};
public NotificationConversationInfo(Context context, AttributeSet attrs) {
@@ -186,7 +186,6 @@
}
public void bindNotification(
- @Action int selectedAction,
ShortcutManager shortcutManager,
PackageManager pm,
PeopleSpaceWidgetManager peopleSpaceWidgetManager,
@@ -205,8 +204,6 @@
OnConversationSettingsClickListener onConversationSettingsClickListener,
Optional<BubblesManager> bubblesManagerOptional,
ShadeController shadeController) {
- mPressedApply = false;
- mSelectedAction = selectedAction;
mINotificationManager = iNotificationManager;
mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
mOnUserInteractionCallback = onUserInteractionCallback;
@@ -417,9 +414,7 @@
}
@Override
- public void onFinishedClosing() {
- mSelectedAction = -1;
- }
+ public void onFinishedClosing() { }
@Override
public boolean needsFalsingProtection() {
@@ -564,7 +559,7 @@
}
@Override
- public boolean shouldBeSaved() {
+ public boolean shouldBeSavedOnClose() {
return mPressedApply;
}
@@ -578,6 +573,12 @@
if (save && mSelectedAction > -1) {
updateChannel();
}
+
+ // Clear the selected importance when closing, so when when we open again,
+ // we starts from a clean state.
+ mSelectedAction = -1;
+ mPressedApply = false;
+
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index fc296e1..93f0812 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -76,7 +76,7 @@
switch (action) {
case AccessibilityNodeInfo.ACTION_LONG_CLICK:
- closeControls(host, false);
+ closeControls(host, /* save= */ false);
return true;
}
@@ -123,7 +123,7 @@
/**
* Return whether something changed and needs to be saved, possibly requiring a bouncer.
*/
- boolean shouldBeSaved();
+ boolean shouldBeSavedOnClose();
/**
* Called when the guts view has finished its close animation.
@@ -259,7 +259,7 @@
if (mGutsContent != null) {
if ((mGutsContent.isLeavebehind() && leavebehinds)
|| (!mGutsContent.isLeavebehind() && controls)) {
- closeControls(x, y, mGutsContent.shouldBeSaved(), force);
+ closeControls(x, y, mGutsContent.shouldBeSavedOnClose(), force);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 7b0b0ce..ea12b82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -463,7 +463,6 @@
R.dimen.notification_guts_conversation_icon_size));
notificationInfoView.bindNotification(
- notificationInfoView.getSelectedAction(),
mShortcutManager,
pmUser,
mPeopleSpaceWidgetManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 8b01a47..ea0060a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -158,7 +158,7 @@
// used by standard ui
private OnClickListener mOnDismissSettings = v -> {
mPressedApply = true;
- mGutsContainer.closeControls(v, true);
+ mGutsContainer.closeControls(v, /* save= */ true);
};
public NotificationInfo(Context context, AttributeSet attrs) {
@@ -541,10 +541,6 @@
@Override
public void onFinishedClosing() {
- if (mChosenImportance != null) {
- mStartingChannelImportance = mChosenImportance;
- }
-
bindInlineControls();
logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_CLOSE);
@@ -604,7 +600,7 @@
}
@Override
- public boolean shouldBeSaved() {
+ public boolean shouldBeSavedOnClose() {
return mPressedApply;
}
@@ -627,6 +623,12 @@
if (save) {
saveImportance();
}
+
+ // Clear the selected importance when closing, so when when we open again,
+ // we starts from a clean state.
+ mChosenImportance = null;
+ mPressedApply = false;
+
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 512b049..adbfa75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -384,7 +384,7 @@
private void undoSnooze(View v) {
mSelectedOption = null;
showSnoozeOptions(false);
- mGutsContainer.closeControls(v, false);
+ mGutsContainer.closeControls(v, /* save= */ false);
}
@Override
@@ -433,7 +433,7 @@
}
@Override
- public boolean shouldBeSaved() {
+ public boolean shouldBeSavedOnClose() {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index 186ffa6..ac97e77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -16,22 +16,13 @@
package com.android.systemui.statusbar.notification.row;
-import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
import android.app.INotificationManager;
-import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
@@ -46,8 +37,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import java.lang.annotation.Retention;
-import java.util.List;
import java.util.Set;
/**
@@ -71,8 +60,6 @@
private Set<NotificationChannel> mUniqueChannelsInRow;
private Drawable mPkgIcon;
- private @Action int mSelectedAction = -1;
- private boolean mPressedApply;
private boolean mPresentingChannelEditorDialog = false;
private NotificationInfo.OnSettingsClickListener mOnSettingsClickListener;
@@ -82,14 +69,8 @@
@VisibleForTesting
boolean mSkipPost = false;
- @Retention(SOURCE)
- @IntDef({ACTION_SETTINGS})
- private @interface Action {}
- static final int ACTION_SETTINGS = 5;
-
private OnClickListener mOnDone = v -> {
- mPressedApply = true;
- mGutsContainer.closeControls(v, true);
+ mGutsContainer.closeControls(v, /* save= */ false);
};
public PartialConversationInfo(Context context, AttributeSet attrs) {
@@ -107,7 +88,6 @@
NotificationInfo.OnSettingsClickListener onSettingsClick,
boolean isDeviceProvisioned,
boolean isNonBlockable) {
- mSelectedAction = -1;
mINotificationManager = iNotificationManager;
mPackageName = pkg;
mSbn = entry.getSbn();
@@ -286,8 +266,8 @@
}
@Override
- public boolean shouldBeSaved() {
- return mPressedApply;
+ public boolean shouldBeSavedOnClose() {
+ return false;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index dbc5f7c..171d893 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -241,5 +241,5 @@
private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
.addFeature("feature")
- .setPackageName(PACKAGE_NAME)
+ .setClientPackageName(PACKAGE_NAME)
.build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index cd8ee73..1061e3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -686,5 +686,5 @@
private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
.addFeature("feature")
- .setPackageName(PACKAGE_NAME)
+ .setClientPackageName(PACKAGE_NAME)
.build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 381d72f..90adabf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -236,7 +236,6 @@
@Test
public void testBindNotification_SetsShortcutIcon() {
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -261,7 +260,6 @@
public void testBindNotification_SetsTextApplicationName() {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -314,7 +312,6 @@
mConversationChannel.setGroup(group.getId());
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -340,7 +337,6 @@
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() {
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -365,7 +361,6 @@
@Test
public void testBindNotification_noDelegate() {
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -401,7 +396,6 @@
.setShortcutInfo(mShortcutInfo)
.build();
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -427,7 +421,6 @@
public void testBindNotification_SetsOnClickListenerForSettings() {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -457,7 +450,6 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() {
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -482,7 +474,6 @@
public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned() {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -511,7 +502,6 @@
mConversationChannel.setImportance(IMPORTANCE_LOW);
mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -540,7 +530,6 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -572,7 +561,6 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -610,7 +598,6 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -639,7 +626,6 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -675,7 +661,6 @@
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -704,7 +689,6 @@
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -735,7 +719,7 @@
.isEqualTo(GONE);
// no changes until hit done
- assertFalse(mNotificationInfo.shouldBeSaved());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
assertFalse(mConversationChannel.isImportantConversation());
@@ -749,7 +733,6 @@
mConversationChannel.setImportance(IMPORTANCE_LOW);
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -779,7 +762,7 @@
.isEqualTo(GONE);
// no changes until hit done
- assertFalse(mNotificationInfo.shouldBeSaved());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
assertFalse(mConversationChannel.isImportantConversation());
@@ -793,7 +776,6 @@
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -825,7 +807,7 @@
.isEqualTo(VISIBLE);
// no changes until save
- assertFalse(mNotificationInfo.shouldBeSaved());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), anyInt(), any());
assertEquals(IMPORTANCE_DEFAULT, mConversationChannel.getImportance());
@@ -838,7 +820,6 @@
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -868,6 +849,7 @@
assertTrue(captor.getValue().isImportantConversation());
assertTrue(captor.getValue().canBubble());
assertEquals(IMPORTANCE_DEFAULT, captor.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -876,7 +858,6 @@
mConversationChannel.setImportance(9);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -913,7 +894,6 @@
mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -954,7 +934,6 @@
// WHEN we indicate no selected action
mNotificationInfo.bindNotification(
- -1, // no action selected by default
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -984,8 +963,8 @@
mConversationChannel.setImportantConversation(false);
// WHEN we indicate the selected action should be "Favorite"
+ mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
mNotificationInfo.bindNotification(
- NotificationConversationInfo.ACTION_FAVORITE, // "Favorite" selected by default
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1015,7 +994,6 @@
mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1044,6 +1022,7 @@
assertFalse(captor.getValue().isImportantConversation());
assertFalse(captor.getValue().canBubble());
assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1052,7 +1031,6 @@
mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1089,7 +1067,6 @@
mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1125,7 +1102,6 @@
mConversationChannel.setAllowBubbles(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1155,12 +1131,46 @@
assertFalse(captor.getValue().isImportantConversation());
assertFalse(captor.getValue().canBubble());
assertEquals(IMPORTANCE_LOW, captor.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
+ }
+
+ @Test
+ public void testSilence_closeGutsThenTryToSave() {
+ mConversationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mConversationChannel.setImportantConversation(true);
+ mConversationChannel.setAllowBubbles(true);
+
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mMockPackageManager,
+ mPeopleSpaceWidgetManager,
+ mMockINotificationManager,
+ mOnUserInteractionCallback,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ mBubbleMetadata,
+ null,
+ mIconFactory,
+ mContext,
+ true,
+ mTestHandler,
+ mTestHandler, null, Optional.of(mBubblesManager),
+ mShadeController);
+
+ mNotificationInfo.findViewById(R.id.silence).performClick();
+ mNotificationInfo.handleCloseControls(false, false);
+ mNotificationInfo.findViewById(R.id.done).performClick();
+
+ mTestableLooper.processAllMessages();
+
+ assertEquals(IMPORTANCE_DEFAULT, mConversationChannel.getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
public void testBindNotification_createsNewChannel() throws Exception {
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1186,7 +1196,6 @@
public void testBindNotification_doesNotCreateNewChannelIfExists() throws Exception {
mNotificationChannel.setConversationId("", CONVERSATION_ID);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1213,7 +1222,6 @@
//WHEN channel is default importance
mNotificationChannel.setImportantConversation(false);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1244,7 +1252,6 @@
@Test
public void testSelectDefaultDoesNotRequestPinPeopleTile() {
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
@@ -1279,7 +1286,6 @@
mConversationChannel.setImportantConversation(true);
mNotificationInfo.bindNotification(
- -1,
mShortcutManager,
mMockPackageManager,
mPeopleSpaceWidgetManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
new file mode 100644
index 0000000..e696c87
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.statusbar.notification.row
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.After
+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.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationGutsTest : SysuiTestCase() {
+
+ private lateinit var guts: NotificationGuts
+ private lateinit var gutsContentView: View
+
+ @Mock
+ private lateinit var gutsContent: NotificationGuts.GutsContent
+
+ @Mock
+ private lateinit var gutsClosedListener: NotificationGuts.OnGutsClosedListener
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ val layoutInflater = LayoutInflater.from(mContext)
+ guts = layoutInflater.inflate(R.layout.notification_guts, null) as NotificationGuts
+ gutsContentView = View(mContext)
+
+ whenever(gutsContent.contentView).thenReturn(gutsContentView)
+
+ ViewUtils.attachView(guts)
+ }
+
+ @After
+ fun tearDown() {
+ ViewUtils.detachView(guts)
+ }
+
+ @Test
+ fun setGutsContent() {
+ guts.gutsContent = gutsContent
+
+ verify(gutsContent).setGutsParent(guts)
+ }
+
+ @Test
+ fun openControls() {
+ guts.gutsContent = gutsContent
+
+ guts.openControls(true, 0, 0, false, null)
+ }
+
+ @Test
+ fun closeControlsWithSave() {
+ guts.gutsContent = gutsContent
+ guts.setClosedListener(gutsClosedListener)
+
+ guts.closeControls(gutsContentView, true)
+
+ verify(gutsContent).handleCloseControls(true, false)
+ verify(gutsClosedListener).onGutsClosed(guts)
+ }
+
+ @Test
+ fun closeControlsWithoutSave() {
+ guts.gutsContent = gutsContent
+ guts.setClosedListener(gutsClosedListener)
+
+ guts.closeControls(gutsContentView, false)
+
+ verify(gutsContent).handleCloseControls(false, false)
+ verify(gutsClosedListener).onGutsClosed(guts)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index b1f1075..80a81a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -50,6 +50,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
@@ -1090,6 +1091,7 @@
mUiEventLogger.eventId(0));
assertEquals(NotificationControlsEvent.NOTIFICATION_CONTROLS_SAVE_IMPORTANCE.getId(),
mUiEventLogger.eventId(1));
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1124,6 +1126,7 @@
assertTrue((updated.getValue().getUserLockedFields()
& USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1156,6 +1159,7 @@
verify(mMockINotificationManager, times(1)).unlockNotificationChannel(
anyString(), eq(TEST_UID), any());
assertEquals(IMPORTANCE_DEFAULT, mNotificationChannel.getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1191,6 +1195,7 @@
assertTrue((updated.getValue().getUserLockedFields()
& USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1229,6 +1234,37 @@
anyString(), eq(TEST_UID), updated.capture());
assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_MIN, updated.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
+ }
+
+ @Test
+ public void testSilence_closeGutsThenTryToSave() throws RemoteException {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mOnUserInteractionCallback,
+ mChannelEditorDialogController,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mEntry,
+ null,
+ null,
+ mUiEventLogger,
+ true,
+ false,
+ false,
+ mAssistantFeedbackController);
+
+ mNotificationInfo.findViewById(R.id.silence).performClick();
+ mNotificationInfo.handleCloseControls(false, false);
+ mNotificationInfo.handleCloseControls(true, false);
+
+ mTestableLooper.processAllMessages();
+
+ assertEquals(IMPORTANCE_DEFAULT, mNotificationChannel.getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1267,6 +1303,7 @@
anyString(), eq(TEST_UID), updated.capture());
assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1294,6 +1331,7 @@
mNotificationInfo.handleCloseControls(true, false);
verify(mOnUserInteractionCallback).onImportanceChanged(mEntry);
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1360,6 +1398,7 @@
assertTrue((updated.getValue().getUserLockedFields()
& USER_LOCKED_IMPORTANCE) != 0);
assertEquals(IMPORTANCE_DEFAULT, updated.getValue().getImportance());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
@@ -1450,7 +1489,7 @@
mNotificationInfo.findViewById(R.id.alert).performClick();
- assertFalse(mNotificationInfo.shouldBeSaved());
+ assertFalse(mNotificationInfo.shouldBeSavedOnClose());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index 43aa8fe..12c8fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
-import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
import static android.view.View.GONE;
@@ -25,7 +24,6 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -36,8 +34,6 @@
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
-import android.app.Person;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 23f0ffb..351a1e9 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -39,7 +39,6 @@
public class BroadcastRadioService extends SystemService {
private static final String TAG = "BcRadioSrv";
- private static final boolean DEBUG = false;
private final ServiceImpl mServiceImpl = new ServiceImpl();
@@ -74,6 +73,7 @@
@Override
public List<RadioManager.ModuleProperties> listModules() {
+ Slog.v(TAG, "Listing HIDL modules");
enforcePolicyAccess();
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
modules.addAll(mV1Modules);
@@ -84,7 +84,7 @@
@Override
public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
boolean withAudio, ITunerCallback callback) throws RemoteException {
- if (DEBUG) Slog.i(TAG, "Opening module " + moduleId);
+ Slog.v(TAG, "Opening module " + moduleId);
enforcePolicyAccess();
if (callback == null) {
throw new IllegalArgumentException("Callback must not be empty");
@@ -101,16 +101,14 @@
@Override
public ICloseHandle addAnnouncementListener(int[] enabledTypes,
IAnnouncementListener listener) {
- if (DEBUG) {
- Slog.i(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
- }
+ Slog.v(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
Objects.requireNonNull(enabledTypes);
Objects.requireNonNull(listener);
enforcePolicyAccess();
synchronized (mLock) {
if (!mHal2.hasAnyModules()) {
- Slog.i(TAG, "There are no HAL 2.x modules registered");
+ Slog.i(TAG, "There are no HAL 2.0 modules registered");
return new AnnouncementAggregator(listener, mLock);
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5c07f76..534e828 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -132,6 +132,7 @@
}
public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
+ Slog.v(TAG, "List HIDL 2.0 modules");
synchronized (mLock) {
return mModules.values().stream().map(module -> module.mProperties)
.collect(Collectors.toList());
@@ -152,10 +153,11 @@
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
+ Slog.v(TAG, "Open HIDL 2.0 session");
Objects.requireNonNull(callback);
if (!withAudio) {
- throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.x");
+ throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0");
}
RadioModule module = null;
@@ -175,6 +177,7 @@
public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull IAnnouncementListener listener) {
+ Slog.v(TAG, "Add announcementListener");
AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
boolean anySupported = false;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index ef7f4c9..aeaa678 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -142,8 +142,12 @@
public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
Object lock) {
try {
+ Slog.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
IBroadcastRadio service = IBroadcastRadio.getService(fqName);
- if (service == null) return null;
+ if (service == null) {
+ Slog.w(TAG, "No service found for fqName " + fqName);
+ return null;
+ }
Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
service.getAmFmRegionConfig(false, (result, config) -> {
@@ -160,7 +164,7 @@
return new RadioModule(service, prop, lock);
} catch (RemoteException ex) {
- Slog.e(TAG, "failed to load module " + fqName, ex);
+ Slog.e(TAG, "Failed to load module " + fqName, ex);
return null;
}
}
@@ -171,6 +175,7 @@
public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
throws RemoteException {
+ Slog.i(TAG, "Open TunerSession");
synchronized (mLock) {
if (mHalTunerSession == null) {
Mutable<ITunerSession> hwSession = new Mutable<>();
@@ -201,6 +206,7 @@
// Copy the contents of mAidlTunerSessions into a local array because TunerSession.close()
// must be called without mAidlTunerSessions locked because it can call
// onTunerSessionClosed().
+ Slog.i(TAG, "Close TunerSessions");
TunerSession[] tunerSessions;
synchronized (mLock) {
tunerSessions = new TunerSession[mAidlTunerSessions.size()];
@@ -313,7 +319,7 @@
}
onTunerSessionProgramListFilterChanged(null);
if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
- Slog.v(TAG, "closing HAL tuner session");
+ Slog.i(TAG, "Closing HAL tuner session");
try {
mHalTunerSession.close();
} catch (RemoteException ex) {
@@ -365,6 +371,7 @@
public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException {
+ Slog.i(TAG, "Add AnnouncementListener");
ArrayList<Byte> enabledList = new ArrayList<>();
for (int type : enabledTypes) {
enabledList.add((byte)type);
@@ -401,6 +408,7 @@
}
Bitmap getImage(int id) {
+ Slog.i(TAG, "Get image for id " + id);
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index d476fd6..c13216b 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -27,6 +27,7 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
import android.util.Slog;
@@ -61,8 +62,13 @@
mLock = Objects.requireNonNull(lock);
}
+ private boolean isDebugEnabled() {
+ return Log.isLoggable(TAG, Log.DEBUG);
+ }
+
@Override
public void close() {
+ if (isDebugEnabled()) Slog.d(TAG, "Close");
close(null);
}
@@ -74,6 +80,7 @@
* @param error Optional error to send to client before session is closed.
*/
public void close(@Nullable Integer error) {
+ if (isDebugEnabled()) Slog.d(TAG, "Close on error " + error);
synchronized (mLock) {
if (mIsClosed) return;
if (error != null) {
@@ -104,7 +111,7 @@
synchronized (mLock) {
checkNotClosedLocked();
mDummyConfig = Objects.requireNonNull(config);
- Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
+ Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
}
}
@@ -137,6 +144,10 @@
@Override
public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Step with directionDown " + directionDown
+ + " skipSubChannel " + skipSubChannel);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.step(!directionDown);
@@ -146,6 +157,10 @@
@Override
public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Scan with directionDown " + directionDown
+ + " skipSubChannel " + skipSubChannel);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.scan(!directionDown, skipSubChannel);
@@ -155,6 +170,7 @@
@Override
public void tune(ProgramSelector selector) throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "Tune with selector " + selector);
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
@@ -164,6 +180,7 @@
@Override
public void cancel() {
+ Slog.i(TAG, "Cancel");
synchronized (mLock) {
checkNotClosedLocked();
Utils.maybeRethrow(mHwSession::cancel);
@@ -172,23 +189,25 @@
@Override
public void cancelAnnouncement() {
- Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x");
+ Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in HAL 2.0");
}
@Override
public Bitmap getImage(int id) {
+ if (isDebugEnabled()) Slog.d(TAG, "Get image for " + id);
return mModule.getImage(id);
}
@Override
public boolean startBackgroundScan() {
- Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
+ Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.0");
mModule.fanoutAidlCallback(cb -> cb.onBackgroundScanComplete());
return true;
}
@Override
public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "start programList updates " + filter);
// If the AIDL client provides a null filter, it wants all updates, so use the most broad
// filter.
if (filter == null) {
@@ -247,6 +266,7 @@
@Override
public void stopProgramListUpdates() throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "Stop programList updates");
synchronized (mLock) {
checkNotClosedLocked();
mProgramInfoCache = null;
@@ -270,7 +290,7 @@
@Override
public boolean isConfigFlagSet(int flag) {
- Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
+ if (isDebugEnabled()) Slog.d(TAG, "Is ConfigFlagSet for " + ConfigFlag.toString(flag));
synchronized (mLock) {
checkNotClosedLocked();
@@ -292,7 +312,9 @@
@Override
public void setConfigFlag(int flag, boolean value) throws RemoteException {
- Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Set ConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.setConfigFlag(flag, value);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 72464be..db646df 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -993,8 +993,9 @@
final String packageName = info.topActivity.getPackageName();
// If the app didn't change, there's nothing to do. Otherwise, we have to
// update the category and re-apply the brightness correction.
- if (mForegroundAppPackageName != null
- && mForegroundAppPackageName.equals(packageName)) {
+ String currentForegroundAppPackageName = mForegroundAppPackageName;
+ if (currentForegroundAppPackageName != null
+ && currentForegroundAppPackageName.equals(packageName)) {
return;
}
mPendingForegroundAppPackageName = packageName;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 85b0149..0c4273f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7051,7 +7051,8 @@
mResolveActivity.processName = pkg.getProcessName();
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
- | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+ | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS
+ | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
mResolveActivity.theme = 0;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
@@ -7084,7 +7085,8 @@
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
- | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+ | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY
+ | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index 12e68b1..eebd046 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -96,6 +96,7 @@
"Turning off vibrator " + getVibratorId());
}
controller.off();
+ getVibration().stats().reportVibratorOff();
}
protected void changeAmplitude(float amplitude) {
@@ -104,6 +105,7 @@
"Amplitude changed on vibrator " + getVibratorId() + " to " + amplitude);
}
controller.setAmplitude(amplitude);
+ getVibration().stats().reportSetAmplitude();
}
/**
@@ -147,6 +149,8 @@
if (nextSegmentIndex >= effectSize && repeatIndex >= 0) {
// Count the loops that were played.
int loopSize = effectSize - repeatIndex;
+ int loopSegmentsPlayed = nextSegmentIndex - repeatIndex;
+ getVibration().stats().reportRepetition(loopSegmentsPlayed / loopSize);
nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
}
Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index 3bc11c8..f8b9926 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -67,9 +67,10 @@
Slog.d(VibrationThread.TAG, "Compose " + primitives + " primitives on vibrator "
+ controller.getVibratorInfo().getId());
}
- mVibratorOnResult = controller.on(
- primitives.toArray(new PrimitiveSegment[primitives.size()]),
- getVibration().id);
+ PrimitiveSegment[] primitivesArray =
+ primitives.toArray(new PrimitiveSegment[primitives.size()]);
+ mVibratorOnResult = controller.on(primitivesArray, getVibration().id);
+ getVibration().stats().reportComposePrimitives(mVibratorOnResult, primitivesArray);
return nextSteps(/* segmentsPlayed= */ primitives.size());
} finally {
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index 919f1be..81f52c9 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -68,8 +68,9 @@
Slog.d(VibrationThread.TAG, "Compose " + pwles + " PWLEs on vibrator "
+ controller.getVibratorInfo().getId());
}
- mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
- getVibration().id);
+ RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
+ mVibratorOnResult = controller.on(pwlesArray, getVibration().id);
+ getVibration().stats().reportComposePwle(mVibratorOnResult, pwlesArray);
return nextSteps(/* segmentsPlayed= */ pwles.size());
} finally {
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
index 601ae97..419021478 100644
--- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -62,6 +62,7 @@
VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
mVibratorOnResult = controller.on(prebaked, getVibration().id);
+ getVibration().stats().reportPerformEffect(mVibratorOnResult, prebaked);
if (mVibratorOnResult == 0 && prebaked.shouldFallback()
&& (fallback instanceof VibrationEffect.Composed)) {
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index 1f0d2d7..6fb9111 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -148,7 +148,9 @@
"Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ duration + "ms");
}
- return controller.on(duration, getVibration().id);
+ long vibratorOnResult = controller.on(duration, getVibration().id);
+ getVibration().stats().reportVibratorOn(vibratorOnResult);
+ return vibratorOnResult;
}
/**
diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
index 080a36c..2c6fbbc9 100644
--- a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
+++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
@@ -93,10 +93,8 @@
}
mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
- if (mVibratorsOnMaxDuration > 0) {
- conductor.vibratorManagerHooks.noteVibratorOn(conductor.getVibration().uid,
- mVibratorsOnMaxDuration);
- }
+ conductor.vibratorManagerHooks.noteVibratorOn(conductor.getVibration().uid,
+ mVibratorsOnMaxDuration);
} finally {
if (mVibratorsOnMaxDuration >= 0) {
// It least one vibrator was started then add a finish step to wait for all
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index d79837b..a375d0a 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -16,10 +16,10 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.CombinedVibration;
import android.os.IBinder;
-import android.os.SystemClock;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.vibrator.PrebakedSegment;
@@ -30,48 +30,60 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
/** Represents a vibration request to the vibrator service. */
final class Vibration {
- private static final String TAG = "Vibration";
private static final SimpleDateFormat DEBUG_DATE_FORMAT =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */
enum Status {
- RUNNING,
- FINISHED,
- FINISHED_UNEXPECTED, // Didn't terminate in the usual way.
- FORWARDED_TO_INPUT_DEVICES,
- CANCELLED_BINDER_DIED,
- CANCELLED_BY_SCREEN_OFF,
- CANCELLED_BY_SETTINGS_UPDATE,
- CANCELLED_BY_USER,
- CANCELLED_BY_UNKNOWN_REASON,
- CANCELLED_SUPERSEDED,
- IGNORED_ERROR_APP_OPS,
- IGNORED_ERROR_CANCELLING,
- IGNORED_ERROR_SCHEDULING,
- IGNORED_ERROR_TOKEN,
- IGNORED_APP_OPS,
- IGNORED_BACKGROUND,
- IGNORED_UNKNOWN_VIBRATION,
- IGNORED_UNSUPPORTED,
- IGNORED_FOR_EXTERNAL,
- IGNORED_FOR_HIGHER_IMPORTANCE,
- IGNORED_FOR_ONGOING,
- IGNORED_FOR_POWER,
- IGNORED_FOR_RINGER_MODE,
- IGNORED_FOR_SETTINGS,
- IGNORED_SUPERSEDED,
+ UNKNOWN(VibrationProto.UNKNOWN),
+ RUNNING(VibrationProto.RUNNING),
+ FINISHED(VibrationProto.FINISHED),
+ FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED),
+ FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES),
+ CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED),
+ CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF),
+ CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE),
+ CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER),
+ CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON),
+ CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED),
+ IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS),
+ IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING),
+ IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING),
+ IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN),
+ IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS),
+ IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND),
+ IGNORED_UNKNOWN_VIBRATION(VibrationProto.IGNORED_UNKNOWN_VIBRATION),
+ IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED),
+ IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL),
+ IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE),
+ IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING),
+ IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER),
+ IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE),
+ IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS),
+ IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED);
+
+ private final int mProtoEnumValue;
+
+ Status(int value) {
+ mProtoEnumValue = value;
+ }
+
+ public int getProtoEnumValue() {
+ return mProtoEnumValue;
+ }
}
- /** Start time using {@link SystemClock#uptimeMillis()}, for calculations. */
- public final long startUptimeMillis;
public final VibrationAttributes attrs;
public final long id;
public final int uid;
@@ -91,17 +103,11 @@
@Nullable
private CombinedVibration mOriginalEffect;
- /**
- * Start/end times in unix epoch time. Only to be used for debugging purposes and to correlate
- * with other system events, any duration calculations should be done use
- * {@link #startUptimeMillis} so as not to be affected by discontinuities created by RTC
- * adjustments.
- */
- private final long mStartTimeDebug;
- private long mEndTimeDebug;
- /** End time using {@link SystemClock#uptimeMillis()}, for calculations. */
- private long mEndUptimeMillis;
- private Status mStatus;
+ /** Vibration status. */
+ private Vibration.Status mStatus;
+
+ /** Vibration runtime stats. */
+ private final VibrationStats mStats = new VibrationStats();
/** A {@link CountDownLatch} to enable waiting for completion. */
private final CountDownLatch mCompletionLatch = new CountDownLatch(1);
@@ -111,34 +117,35 @@
this.token = token;
this.mEffect = effect;
this.id = id;
- this.startUptimeMillis = SystemClock.uptimeMillis();
this.attrs = attrs;
this.uid = uid;
this.opPkg = opPkg;
this.reason = reason;
- mStartTimeDebug = System.currentTimeMillis();
- mStatus = Status.RUNNING;
+ mStatus = Vibration.Status.RUNNING;
+ }
+
+ VibrationStats stats() {
+ return mStats;
}
/**
- * Set the {@link Status} of this vibration and the current system time as this
+ * Set the {@link Status} of this vibration and reports the current system time as this
* vibration end time, for debugging purposes.
*
* <p>This method will only accept given value if the current status is {@link
* Status#RUNNING}.
*/
- public void end(Status status) {
+ public void end(EndInfo info) {
if (hasEnded()) {
// Vibration already ended, keep first ending status set and ignore this one.
return;
}
- mStatus = status;
- mEndUptimeMillis = SystemClock.uptimeMillis();
- mEndTimeDebug = System.currentTimeMillis();
+ mStatus = info.status;
+ mStats.reportEnded(info.endedByUid, info.endedByUsage);
mCompletionLatch.countDown();
}
- /** Waits indefinitely until another thread calls {@link #end(Status)} on this vibration. */
+ /** Waits indefinitely until another thread calls {@link #end} on this vibration. */
public void waitForEnd() throws InterruptedException {
mCompletionLatch.await();
}
@@ -228,16 +235,69 @@
/** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */
public Vibration.DebugInfo getDebugInfo() {
- long durationMs = hasEnded() ? mEndUptimeMillis - startUptimeMillis : -1;
- return new Vibration.DebugInfo(
- mStartTimeDebug, mEndTimeDebug, durationMs, mEffect, mOriginalEffect,
- /* scale= */ 0, attrs, uid, opPkg, reason, mStatus);
+ return new Vibration.DebugInfo(mStatus, mStats, mEffect, mOriginalEffect, /* scale= */ 0,
+ attrs, uid, opPkg, reason);
+ }
+
+ /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
+ public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
+ int vibrationType = isRepeating()
+ ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
+ : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
+ return new VibrationStats.StatsInfo(
+ uid, vibrationType, attrs.getUsage(), mStatus, mStats, completionUptimeMillis);
+ }
+
+ /** Immutable info passed as a signal to end a vibration. */
+ static final class EndInfo {
+ /** The {@link Status} to be set to the vibration when it ends with this info. */
+ @NonNull
+ public final Status status;
+ /** The UID that triggered the vibration that ended this, or -1 if undefined. */
+ public final int endedByUid;
+ /** The VibrationAttributes.USAGE_* of the vibration that ended this, or -1 if undefined. */
+ public final int endedByUsage;
+
+ EndInfo(@NonNull Vibration.Status status) {
+ this(status, /* endedByUid= */ -1, /* endedByUsage= */ -1);
+ }
+
+ EndInfo(@NonNull Vibration.Status status, int endedByUid, int endedByUsage) {
+ this.status = status;
+ this.endedByUid = endedByUid;
+ this.endedByUsage = endedByUsage;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EndInfo)) return false;
+ EndInfo that = (EndInfo) o;
+ return endedByUid == that.endedByUid
+ && endedByUsage == that.endedByUsage
+ && status == that.status;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(status, endedByUid, endedByUsage);
+ }
+
+ @Override
+ public String toString() {
+ return "EndInfo{"
+ + "status=" + status
+ + ", endedByUid=" + endedByUid
+ + ", endedByUsage=" + endedByUsage
+ + '}';
+ }
}
/** Debug information about vibrations. */
static final class DebugInfo {
- private final long mStartTimeDebug;
- private final long mEndTimeDebug;
+ private final long mCreateTime;
+ private final long mStartTime;
+ private final long mEndTime;
private final long mDurationMs;
private final CombinedVibration mEffect;
private final CombinedVibration mOriginalEffect;
@@ -248,12 +308,13 @@
private final String mReason;
private final Status mStatus;
- DebugInfo(long startTimeDebug, long endTimeDebug, long durationMs,
- CombinedVibration effect, CombinedVibration originalEffect, float scale,
- VibrationAttributes attrs, int uid, String opPkg, String reason, Status status) {
- mStartTimeDebug = startTimeDebug;
- mEndTimeDebug = endTimeDebug;
- mDurationMs = durationMs;
+ DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration effect,
+ @Nullable CombinedVibration originalEffect, float scale, VibrationAttributes attrs,
+ int uid, String opPkg, String reason) {
+ mCreateTime = stats.getCreateTimeDebug();
+ mStartTime = stats.getStartTimeDebug();
+ mEndTime = stats.getEndTimeDebug();
+ mDurationMs = stats.getDurationDebug();
mEffect = effect;
mOriginalEffect = originalEffect;
mScale = scale;
@@ -267,11 +328,13 @@
@Override
public String toString() {
return new StringBuilder()
- .append("startTime: ")
- .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug)))
+ .append("createTime: ")
+ .append(DEBUG_DATE_FORMAT.format(new Date(mCreateTime)))
+ .append(", startTime: ")
+ .append(DEBUG_DATE_FORMAT.format(new Date(mStartTime)))
.append(", endTime: ")
- .append(mEndTimeDebug == 0 ? null
- : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
+ .append(mEndTime == 0 ? null
+ : DEBUG_DATE_FORMAT.format(new Date(mEndTime)))
.append(", durationMs: ")
.append(mDurationMs)
.append(", status: ")
@@ -296,8 +359,8 @@
/** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
public void dumpProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(VibrationProto.START_TIME, mStartTimeDebug);
- proto.write(VibrationProto.END_TIME, mEndTimeDebug);
+ proto.write(VibrationProto.START_TIME, mStartTime);
+ proto.write(VibrationProto.END_TIME, mEndTime);
proto.write(VibrationProto.DURATION_MS, mDurationMs);
proto.write(VibrationProto.STATUS, mStatus.ordinal());
@@ -421,4 +484,5 @@
proto.end(token);
}
}
+
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
new file mode 100644
index 0000000..931be1d
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -0,0 +1,395 @@
+/*
+ * 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.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+/** Holds basic stats about the vibration playback and interaction with the vibrator HAL. */
+final class VibrationStats {
+ static final String TAG = "VibrationStats";
+
+ // Milestone timestamps, using SystemClock.uptimeMillis(), for calculations.
+ // - Create: time a vibration object was created, which is closer to when the service receives a
+ // vibrate request.
+ // - Start: time a vibration started to play, which is closer to the time that the
+ // VibrationEffect started playing the very first segment.
+ // - End: time a vibration ended, even if it never started to play. This can be as soon as the
+ // vibrator HAL reports it has finished the last command, or before it has even started
+ // when the vibration is ignored or cancelled.
+ // Create and end times set by VibratorManagerService only, guarded by its lock.
+ // Start times set by VibrationThread only (single-threaded).
+ private long mCreateUptimeMillis;
+ private long mStartUptimeMillis;
+ private long mEndUptimeMillis;
+
+ // Milestone timestamps, using unix epoch time, only to be used for debugging purposes and
+ // to correlate with other system events. Any duration calculations should be done with the
+ // {create/start/end}UptimeMillis counterparts so as not to be affected by discontinuities
+ // created by RTC adjustments.
+ // Set together with the *UptimeMillis counterparts.
+ private long mCreateTimeDebug;
+ private long mStartTimeDebug;
+ private long mEndTimeDebug;
+
+ // Vibration interruption tracking.
+ // Set by VibratorManagerService only, guarded by its lock.
+ private int mEndedByUid;
+ private int mEndedByUsage;
+ private int mInterruptedUsage;
+
+ // All following counters are set by VibrationThread only (single-threaded):
+ // Counts how many times the VibrationEffect was repeated.
+ private int mRepeatCount;
+ // Total duration, in milliseconds, the vibrator was active with non-zero amplitude.
+ private int mVibratorOnTotalDurationMillis;
+ // Total number of primitives used in compositions.
+ private int mVibrationCompositionTotalSize;
+ private int mVibrationPwleTotalSize;
+ // Counts how many times each IVibrator method was triggered by this vibration.
+ private int mVibratorOnCount;
+ private int mVibratorOffCount;
+ private int mVibratorSetAmplitudeCount;
+ private int mVibratorSetExternalControlCount;
+ private int mVibratorPerformCount;
+ private int mVibratorComposeCount;
+ private int mVibratorComposePwleCount;
+
+ // Ids of vibration effects and primitives used by this vibration, with support flag.
+ // Set by VibrationThread only (single-threaded).
+ private SparseBooleanArray mVibratorEffectsUsed = new SparseBooleanArray();
+ private SparseBooleanArray mVibratorPrimitivesUsed = new SparseBooleanArray();
+
+ VibrationStats() {
+ mCreateUptimeMillis = SystemClock.uptimeMillis();
+ mCreateTimeDebug = System.currentTimeMillis();
+ // Set invalid UID and VibrationAttributes.USAGE values to indicate fields are unset.
+ mEndedByUid = -1;
+ mEndedByUsage = -1;
+ mInterruptedUsage = -1;
+ }
+
+ long getCreateUptimeMillis() {
+ return mCreateUptimeMillis;
+ }
+
+ long getStartUptimeMillis() {
+ return mStartUptimeMillis;
+ }
+
+ long getEndUptimeMillis() {
+ return mEndUptimeMillis;
+ }
+
+ long getCreateTimeDebug() {
+ return mCreateTimeDebug;
+ }
+
+ long getStartTimeDebug() {
+ return mStartTimeDebug;
+ }
+
+ long getEndTimeDebug() {
+ return mEndTimeDebug;
+ }
+
+ /**
+ * Duration calculated for debugging purposes, between the creation of a vibration and the
+ * end time being reported, or -1 if the vibration has not ended.
+ */
+ long getDurationDebug() {
+ return hasEnded() ? (mEndUptimeMillis - mCreateUptimeMillis) : -1;
+ }
+
+ /** Return true if vibration reported it has ended. */
+ boolean hasEnded() {
+ return mEndUptimeMillis > 0;
+ }
+
+ /** Return true if vibration reported it has started triggering the vibrator. */
+ boolean hasStarted() {
+ return mStartUptimeMillis > 0;
+ }
+
+ /**
+ * Set the current system time as this vibration start time, for debugging purposes.
+ *
+ * <p>This indicates the vibration has started to interact with the vibrator HAL and the
+ * device may start vibrating after this point.
+ *
+ * <p>This method will only accept given value if the start timestamp was never set.
+ */
+ void reportStarted() {
+ if (hasEnded() || (mStartUptimeMillis != 0)) {
+ // Vibration already started or ended, keep first time set and ignore this one.
+ return;
+ }
+ mStartUptimeMillis = SystemClock.uptimeMillis();
+ mStartTimeDebug = System.currentTimeMillis();
+ }
+
+ /**
+ * Set status and end cause for this vibration to end, and the current system time as this
+ * vibration end time, for debugging purposes.
+ *
+ * <p>This might be triggered before {@link #reportStarted()}, which indicates this
+ * vibration was cancelled or ignored before it started triggering the vibrator.
+ *
+ * @return true if the status was accepted. This method will only accept given values if
+ * the end timestamp was never set.
+ */
+ boolean reportEnded(int endedByUid, int endedByUsage) {
+ if (hasEnded()) {
+ // Vibration already ended, keep first ending stats set and ignore this one.
+ return false;
+ }
+ mEndedByUid = endedByUid;
+ mEndedByUsage = endedByUsage;
+ mEndUptimeMillis = SystemClock.uptimeMillis();
+ mEndTimeDebug = System.currentTimeMillis();
+ return true;
+ }
+
+ /**
+ * Report this vibration has interrupted another vibration.
+ *
+ * <p>This method will only accept the first value as the one that was interrupted by this
+ * vibration, and will ignore all successive calls.
+ */
+ void reportInterruptedAnotherVibration(int interruptedUsage) {
+ if (mInterruptedUsage < 0) {
+ mInterruptedUsage = interruptedUsage;
+ }
+ }
+
+ /** Report the vibration has looped a few more times. */
+ void reportRepetition(int loops) {
+ mRepeatCount += loops;
+ }
+
+ /** Report a call to vibrator method to turn on for given duration. */
+ void reportVibratorOn(long halResult) {
+ mVibratorOnCount++;
+
+ if (halResult > 0) {
+ // If HAL result is positive then it represents the actual duration it will be ON.
+ mVibratorOnTotalDurationMillis += (int) halResult;
+ }
+ }
+
+ /** Report a call to vibrator method to turn off. */
+ void reportVibratorOff() {
+ mVibratorOffCount++;
+ }
+
+ /** Report a call to vibrator method to change the vibration amplitude. */
+ void reportSetAmplitude() {
+ mVibratorSetAmplitudeCount++;
+ }
+
+ /** Report a call to vibrator method to trigger a vibration effect. */
+ void reportPerformEffect(long halResult, PrebakedSegment prebaked) {
+ mVibratorPerformCount++;
+
+ if (halResult > 0) {
+ // If HAL result is positive then it represents the actual duration of the vibration.
+ mVibratorEffectsUsed.put(prebaked.getEffectId(), true);
+ mVibratorOnTotalDurationMillis += (int) halResult;
+ } else {
+ // Effect unsupported or request failed.
+ mVibratorEffectsUsed.put(prebaked.getEffectId(), false);
+ }
+ }
+
+ /** Report a call to vibrator method to trigger a vibration as a composition of primitives. */
+ void reportComposePrimitives(long halResult, PrimitiveSegment[] primitives) {
+ mVibratorComposeCount++;
+ mVibrationCompositionTotalSize += primitives.length;
+
+ if (halResult > 0) {
+ // If HAL result is positive then it represents the actual duration of the vibration.
+ // Remove the requested delays to update the total time the vibrator was ON.
+ for (PrimitiveSegment primitive : primitives) {
+ halResult -= primitive.getDelay();
+ mVibratorPrimitivesUsed.put(primitive.getPrimitiveId(), true);
+ }
+ if (halResult > 0) {
+ mVibratorOnTotalDurationMillis += (int) halResult;
+ }
+ } else {
+ // One or more primitives were unsupported, or request failed.
+ for (PrimitiveSegment primitive : primitives) {
+ mVibratorPrimitivesUsed.put(primitive.getPrimitiveId(), false);
+ }
+ }
+ }
+
+ /** Report a call to vibrator method to trigger a vibration as a PWLE. */
+ void reportComposePwle(long halResult, RampSegment[] segments) {
+ mVibratorComposePwleCount++;
+ mVibrationPwleTotalSize += segments.length;
+
+ if (halResult > 0) {
+ // If HAL result is positive then it represents the actual duration of the vibration.
+ // Remove the zero-amplitude segments to update the total time the vibrator was ON.
+ for (RampSegment ramp : segments) {
+ if ((ramp.getStartAmplitude() == 0) && (ramp.getEndAmplitude() == 0)) {
+ halResult -= ramp.getDuration();
+ }
+ }
+ if (halResult > 0) {
+ mVibratorOnTotalDurationMillis += (int) halResult;
+ }
+ }
+ }
+
+ /**
+ * Increment the stats for total number of times the {@code setExternalControl} method was
+ * triggered in the vibrator HAL.
+ */
+ void reportSetExternalControl() {
+ mVibratorSetExternalControlCount++;
+ }
+
+ /**
+ * Immutable metrics about this vibration, to be kept in memory until it can be pushed through
+ * {@link com.android.internal.util.FrameworkStatsLog} as a
+ * {@link com.android.internal.util.FrameworkStatsLog#VIBRATION_REPORTED}.
+ */
+ static final class StatsInfo {
+ public final int uid;
+ public final int vibrationType;
+ public final int usage;
+ public final int status;
+ public final boolean endedBySameUid;
+ public final int endedByUsage;
+ public final int interruptedUsage;
+ public final int repeatCount;
+ public final int totalDurationMillis;
+ public final int vibratorOnMillis;
+ public final int startLatencyMillis;
+ public final int endLatencyMillis;
+ public final int halComposeCount;
+ public final int halComposePwleCount;
+ public final int halOnCount;
+ public final int halOffCount;
+ public final int halPerformCount;
+ public final int halSetAmplitudeCount;
+ public final int halSetExternalControlCount;
+ public final int halCompositionSize;
+ public final int halPwleSize;
+ public final int[] halSupportedCompositionPrimitivesUsed;
+ public final int[] halSupportedEffectsUsed;
+ public final int[] halUnsupportedCompositionPrimitivesUsed;
+ public final int[] halUnsupportedEffectsUsed;
+ private boolean mIsWritten;
+
+ StatsInfo(int uid, int vibrationType, int usage, Vibration.Status status,
+ VibrationStats stats, long completionUptimeMillis) {
+ this.uid = uid;
+ this.vibrationType = vibrationType;
+ this.usage = usage;
+ this.status = status.getProtoEnumValue();
+ endedBySameUid = (uid == stats.mEndedByUid);
+ endedByUsage = stats.mEndedByUsage;
+ interruptedUsage = stats.mInterruptedUsage;
+ repeatCount = stats.mRepeatCount;
+
+ // This duration goes from the time this object was created until the time it was
+ // completed. We can use latencies to detect the times between first and last
+ // interaction with vibrator.
+ totalDurationMillis =
+ (int) Math.max(0, completionUptimeMillis - stats.mCreateUptimeMillis);
+ vibratorOnMillis = stats.mVibratorOnTotalDurationMillis;
+
+ if (stats.hasStarted()) {
+ // We only measure latencies for vibrations that actually triggered the vibrator.
+ startLatencyMillis =
+ (int) Math.max(0, stats.mStartUptimeMillis - stats.mCreateUptimeMillis);
+ endLatencyMillis =
+ (int) Math.max(0, completionUptimeMillis - stats.mEndUptimeMillis);
+ } else {
+ startLatencyMillis = endLatencyMillis = 0;
+ }
+
+ halComposeCount = stats.mVibratorComposeCount;
+ halComposePwleCount = stats.mVibratorComposePwleCount;
+ halOnCount = stats.mVibratorOnCount;
+ halOffCount = stats.mVibratorOffCount;
+ halPerformCount = stats.mVibratorPerformCount;
+ halSetAmplitudeCount = stats.mVibratorSetAmplitudeCount;
+ halSetExternalControlCount = stats.mVibratorSetExternalControlCount;
+ halCompositionSize = stats.mVibrationCompositionTotalSize;
+ halPwleSize = stats.mVibrationPwleTotalSize;
+ halSupportedCompositionPrimitivesUsed =
+ filteredKeys(stats.mVibratorPrimitivesUsed, /* supported= */ true);
+ halSupportedEffectsUsed =
+ filteredKeys(stats.mVibratorEffectsUsed, /* supported= */ true);
+ halUnsupportedCompositionPrimitivesUsed =
+ filteredKeys(stats.mVibratorPrimitivesUsed, /* supported= */ false);
+ halUnsupportedEffectsUsed =
+ filteredKeys(stats.mVibratorEffectsUsed, /* supported= */ false);
+ }
+
+ @VisibleForTesting
+ boolean isWritten() {
+ return mIsWritten;
+ }
+
+ void writeVibrationReported() {
+ if (mIsWritten) {
+ Slog.wtf(TAG, "Writing same vibration stats multiple times for uid=" + uid);
+ }
+ mIsWritten = true;
+ // Mapping from this MetricInfo representation and the atom proto VibrationReported.
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.VIBRATION_REPORTED,
+ uid, null, vibrationType, usage, status, endedBySameUid, endedByUsage,
+ interruptedUsage, repeatCount, totalDurationMillis, vibratorOnMillis,
+ startLatencyMillis, endLatencyMillis, halComposeCount, halComposePwleCount,
+ halOnCount, halOffCount, halPerformCount, halSetAmplitudeCount,
+ halSetExternalControlCount, halSupportedCompositionPrimitivesUsed,
+ halSupportedEffectsUsed, halUnsupportedCompositionPrimitivesUsed,
+ halUnsupportedEffectsUsed, halCompositionSize, halPwleSize);
+ }
+
+ private static int[] filteredKeys(SparseBooleanArray supportArray, boolean supported) {
+ int count = 0;
+ for (int i = 0; i < supportArray.size(); i++) {
+ if (supportArray.valueAt(i) == supported) count++;
+ }
+ if (count == 0) {
+ return null;
+ }
+ int pos = 0;
+ int[] res = new int[count];
+ for (int i = 0; i < supportArray.size(); i++) {
+ if (supportArray.valueAt(i) == supported) {
+ res[pos++] = supportArray.keyAt(i);
+ }
+ }
+ return res;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index e3d8067..0799b95 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -81,12 +81,12 @@
private final IntArray mSignalVibratorsComplete;
@Nullable
@GuardedBy("mLock")
- private Vibration.Status mSignalCancelStatus = null;
+ private Vibration.EndInfo mSignalCancel = null;
@GuardedBy("mLock")
private boolean mSignalCancelImmediate = false;
@Nullable
- private Vibration.Status mCancelStatus = null;
+ private Vibration.EndInfo mCancelledVibrationEndInfo = null;
private boolean mCancelledImmediately = false; // hard stop
private int mPendingVibrateSteps;
private int mRemainingStartSequentialEffectSteps;
@@ -153,6 +153,9 @@
// This count is decremented at the completion of the step, so we don't subtract one.
mRemainingStartSequentialEffectSteps = sequentialEffect.getEffects().size();
mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect));
+ // Vibration will start playing in the Vibrator, following the effect timings and delays.
+ // Report current time as the vibration start time, for debugging.
+ mVibration.stats().reportStarted();
}
public Vibration getVibration() {
@@ -182,24 +185,25 @@
* Calculate the {@link Vibration.Status} based on the current queue state and the expected
* number of {@link StartSequentialEffectStep} to be played.
*/
- public Vibration.Status calculateVibrationStatus() {
+ @Nullable
+ public Vibration.EndInfo calculateVibrationEndInfo() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- if (mCancelStatus != null) {
- return mCancelStatus;
+ if (mCancelledVibrationEndInfo != null) {
+ return mCancelledVibrationEndInfo;
}
- if (mPendingVibrateSteps > 0
- || mRemainingStartSequentialEffectSteps > 0) {
- return Vibration.Status.RUNNING;
+ if (mPendingVibrateSteps > 0 || mRemainingStartSequentialEffectSteps > 0) {
+ // Vibration still running.
+ return null;
}
// No pending steps, and something happened.
if (mSuccessfulVibratorOnSteps > 0) {
- return Vibration.Status.FINISHED;
+ return new Vibration.EndInfo(Vibration.Status.FINISHED);
}
// If no step was able to turn the vibrator ON successfully.
- return Vibration.Status.IGNORED_UNSUPPORTED;
+ return new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED);
}
/**
@@ -305,45 +309,50 @@
if (DEBUG) {
Slog.d(TAG, "Binder died, cancelling vibration...");
}
- notifyCancelled(Vibration.Status.CANCELLED_BINDER_DIED, /* immediate= */ false);
+ notifyCancelled(new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
+ /* immediate= */ false);
}
/**
* Notify the execution that cancellation is requested. This will be acted upon
* asynchronously in the VibrationThread.
*
+ * <p>Only the first cancel signal will be used to end a cancelled vibration, but subsequent
+ * calls with {@code immediate} flag set to true can still force the first cancel signal to
+ * take effect urgently.
+ *
* @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
*/
- public void notifyCancelled(@NonNull Vibration.Status cancelStatus, boolean immediate) {
+ public void notifyCancelled(@NonNull Vibration.EndInfo cancelInfo, boolean immediate) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(false);
}
if (DEBUG) {
- Slog.d(TAG, "Vibration cancel requested with status=" + cancelStatus
+ Slog.d(TAG, "Vibration cancel requested with signal=" + cancelInfo
+ ", immediate=" + immediate);
}
- if ((cancelStatus == null) || !cancelStatus.name().startsWith("CANCEL")) {
- Slog.w(TAG, "Vibration cancel requested with bad status=" + cancelStatus
+ if ((cancelInfo == null) || !cancelInfo.status.name().startsWith("CANCEL")) {
+ Slog.w(TAG, "Vibration cancel requested with bad signal=" + cancelInfo
+ ", using CANCELLED_UNKNOWN_REASON to ensure cancellation.");
- cancelStatus = Vibration.Status.CANCELLED_BY_UNKNOWN_REASON;
+ cancelInfo = new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_UNKNOWN_REASON);
}
synchronized (mLock) {
- if (immediate && mSignalCancelImmediate || (mSignalCancelStatus != null)) {
+ if ((immediate && mSignalCancelImmediate) || (mSignalCancel != null)) {
if (DEBUG) {
Slog.d(TAG, "Vibration cancel request ignored as the vibration "
- + mVibration.id + "is already being cancelled with status="
- + mSignalCancelStatus + ", immediate=" + mSignalCancelImmediate);
+ + mVibration.id + "is already being cancelled with signal="
+ + mSignalCancel + ", immediate=" + mSignalCancelImmediate);
}
return;
}
mSignalCancelImmediate |= immediate;
- if (mSignalCancelStatus == null) {
- mSignalCancelStatus = cancelStatus;
+ if (mSignalCancel == null) {
+ mSignalCancel = cancelInfo;
} else {
if (DEBUG) {
- Slog.d(TAG, "Vibration cancel request new status=" + cancelStatus
- + " ignored as the vibration was already cancelled with status="
- + mSignalCancelStatus + ", but immediate flag was updated to "
+ Slog.d(TAG, "Vibration cancel request new signal=" + cancelInfo
+ + " ignored as the vibration was already cancelled with signal="
+ + mSignalCancel + ", but immediate flag was updated to "
+ mSignalCancelImmediate);
}
}
@@ -401,9 +410,9 @@
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals.
}
- return (mSignalCancelStatus != mCancelStatus)
- || (mSignalCancelImmediate && !mCancelledImmediately)
- || (mSignalVibratorsComplete.size() > 0);
+ return (mSignalCancel != null && mCancelledVibrationEndInfo == null)
+ || (mSignalCancelImmediate && !mCancelledImmediately)
+ || (mSignalVibratorsComplete.size() > 0);
}
/**
@@ -416,7 +425,7 @@
}
int[] vibratorsToProcess = null;
- Vibration.Status doCancelStatus = null;
+ Vibration.EndInfo doCancelInfo = null;
boolean doCancelImmediate = false;
// Collect signals to process, but don't keep the lock while processing them.
synchronized (mLock) {
@@ -426,10 +435,10 @@
}
// This should only happen once.
doCancelImmediate = true;
- doCancelStatus = mSignalCancelStatus;
+ doCancelInfo = mSignalCancel;
}
- if (mSignalCancelStatus != mCancelStatus) {
- doCancelStatus = mSignalCancelStatus;
+ if ((mSignalCancel != null) && (mCancelledVibrationEndInfo == null)) {
+ doCancelInfo = mSignalCancel;
}
if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
// Swap out the queue of completions to process.
@@ -443,11 +452,11 @@
// completion signals that were collected in this call, but we won't process them
// anyway as all steps are cancelled.
if (doCancelImmediate) {
- processCancelImmediately(doCancelStatus);
+ processCancelImmediately(doCancelInfo);
return;
}
- if (doCancelStatus != null) {
- processCancel(doCancelStatus);
+ if (doCancelInfo != null) {
+ processCancel(doCancelInfo);
}
if (vibratorsToProcess != null) {
processVibratorsComplete(vibratorsToProcess);
@@ -460,12 +469,12 @@
* <p>This will remove all steps and replace them with respective results of
* {@link Step#cancel()}.
*/
- public void processCancel(Vibration.Status cancelStatus) {
+ public void processCancel(Vibration.EndInfo cancelInfo) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- mCancelStatus = cancelStatus;
+ mCancelledVibrationEndInfo = cancelInfo;
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
// and clean up steps are added back to the queue, so they can handle the callback.
List<Step> cleanUpSteps = new ArrayList<>();
@@ -483,13 +492,13 @@
*
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
*/
- public void processCancelImmediately(Vibration.Status cancelStatus) {
+ public void processCancelImmediately(Vibration.EndInfo cancelInfo) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
mCancelledImmediately = true;
- mCancelStatus = cancelStatus;
+ mCancelledVibrationEndInfo = cancelInfo;
Step step;
while ((step = pollNext()) != null) {
step.cancelImmediately();
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index cecc5c0..e824db10 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -76,7 +76,7 @@
* cleanup tasks, and should not be given new work until {@link #onVibrationThreadReleased}
* is called.
*/
- void onVibrationCompleted(long vibrationId, Vibration.Status status);
+ void onVibrationCompleted(long vibrationId, @NonNull Vibration.EndInfo vibrationEndInfo);
/**
* Tells the manager that the VibrationThread is finished with the previous vibration and
@@ -237,7 +237,8 @@
try {
runCurrentVibrationWithWakeLockAndDeathLink();
} finally {
- clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED);
+ clientVibrationCompleteIfNotAlready(
+ new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED));
}
} finally {
mWakeLock.release();
@@ -255,7 +256,8 @@
vibrationBinderToken.linkToDeath(mExecutingConductor, 0);
} catch (RemoteException e) {
Slog.e(TAG, "Error linking vibration to token death", e);
- clientVibrationCompleteIfNotAlready(Vibration.Status.IGNORED_ERROR_TOKEN);
+ clientVibrationCompleteIfNotAlready(
+ new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_TOKEN));
return;
}
// Ensure that the unlink always occurs now.
@@ -274,11 +276,11 @@
// Indicate that the vibration is complete. This can be called multiple times only for
// convenience of handling error conditions - an error after the client is complete won't
// affect the status.
- private void clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus) {
+ private void clientVibrationCompleteIfNotAlready(@NonNull Vibration.EndInfo vibrationEndInfo) {
if (!mCalledVibrationCompleteCallback) {
mCalledVibrationCompleteCallback = true;
mVibratorManagerHooks.onVibrationCompleted(
- mExecutingConductor.getVibration().id, completedStatus);
+ mExecutingConductor.getVibration().id, vibrationEndInfo);
}
}
@@ -298,12 +300,15 @@
mExecutingConductor.runNextStep();
}
- Vibration.Status status = mExecutingConductor.calculateVibrationStatus();
- // This block can only run once due to mCalledVibrationCompleteCallback.
- if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
- // First time vibration stopped running, start clean-up tasks and notify
- // callback immediately.
- clientVibrationCompleteIfNotAlready(status);
+ if (!mCalledVibrationCompleteCallback) {
+ // This block can only run once due to mCalledVibrationCompleteCallback.
+ Vibration.EndInfo vibrationEndInfo =
+ mExecutingConductor.calculateVibrationEndInfo();
+ if (vibrationEndInfo != null) {
+ // First time vibration stopped running, start clean-up tasks and notify
+ // callback immediately.
+ clientVibrationCompleteIfNotAlready(vibrationEndInfo);
+ }
}
}
} finally {
diff --git a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
new file mode 100644
index 0000000..f600a29
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
@@ -0,0 +1,140 @@
+/*
+ * 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.server.vibrator;
+
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/** Helper class for async write of atoms to {@link FrameworkStatsLog} using a given Handler. */
+public class VibratorFrameworkStatsLogger {
+ private static final String TAG = "VibratorFrameworkStatsLogger";
+
+ // VibrationReported pushed atom needs to be throttled to at most one every 10ms.
+ private static final int VIBRATION_REPORTED_MIN_INTERVAL_MILLIS = 10;
+ // We accumulate events that should take 3s to write and drop excessive metrics.
+ private static final int VIBRATION_REPORTED_MAX_QUEUE_SIZE = 300;
+ // Warning about dropping entries after this amount of atoms were dropped by the throttle.
+ private static final int VIBRATION_REPORTED_WARNING_QUEUE_SIZE = 200;
+
+ private final Object mLock = new Object();
+ private final Handler mHandler;
+ private final long mVibrationReportedLogIntervalMillis;
+ private final long mVibrationReportedQueueMaxSize;
+ private final Runnable mConsumeVibrationStatsQueueRunnable =
+ () -> writeVibrationReportedFromQueue();
+
+ @GuardedBy("mLock")
+ private long mLastVibrationReportedLogUptime;
+ @GuardedBy("mLock")
+ private Queue<VibrationStats.StatsInfo> mVibrationStatsQueue = new ArrayDeque<>();
+
+ VibratorFrameworkStatsLogger(Handler handler) {
+ this(handler, VIBRATION_REPORTED_MIN_INTERVAL_MILLIS, VIBRATION_REPORTED_MAX_QUEUE_SIZE);
+ }
+
+ @VisibleForTesting
+ VibratorFrameworkStatsLogger(Handler handler, int vibrationReportedLogIntervalMillis,
+ int vibrationReportedQueueMaxSize) {
+ mHandler = handler;
+ mVibrationReportedLogIntervalMillis = vibrationReportedLogIntervalMillis;
+ mVibrationReportedQueueMaxSize = vibrationReportedQueueMaxSize;
+ }
+
+ /** Writes {@link FrameworkStatsLog#VIBRATOR_STATE_CHANGED} for state ON. */
+ public void writeVibratorStateOnAsync(int uid, long duration) {
+ mHandler.post(
+ () -> FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null,
+ FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, duration));
+ }
+
+ /** Writes {@link FrameworkStatsLog#VIBRATOR_STATE_CHANGED} for state OFF. */
+ public void writeVibratorStateOffAsync(int uid) {
+ mHandler.post(
+ () -> FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null,
+ FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
+ /* duration= */ 0));
+ }
+
+ /**
+ * Writes {@link FrameworkStatsLog#VIBRATION_REPORTED} for given vibration.
+ *
+ * <p>This atom is throttled to be pushed once every 10ms, so this logger can keep a queue of
+ * {@link VibrationStats.StatsInfo} entries to slowly write to statsd.
+ */
+ public void writeVibrationReportedAsync(VibrationStats.StatsInfo metrics) {
+ boolean needsScheduling;
+ long scheduleDelayMs;
+ int queueSize;
+
+ synchronized (mLock) {
+ queueSize = mVibrationStatsQueue.size();
+ needsScheduling = (queueSize == 0);
+
+ if (queueSize < mVibrationReportedQueueMaxSize) {
+ mVibrationStatsQueue.offer(metrics);
+ }
+
+ long nextLogUptime =
+ mLastVibrationReportedLogUptime + mVibrationReportedLogIntervalMillis;
+ scheduleDelayMs = Math.max(0, nextLogUptime - SystemClock.uptimeMillis());
+ }
+
+ if ((queueSize + 1) == VIBRATION_REPORTED_WARNING_QUEUE_SIZE) {
+ Slog.w(TAG, " Approaching vibration metrics queue limit, events might be dropped.");
+ }
+
+ if (needsScheduling) {
+ mHandler.postDelayed(mConsumeVibrationStatsQueueRunnable, scheduleDelayMs);
+ }
+ }
+
+ /** Writes next {@link FrameworkStatsLog#VIBRATION_REPORTED} from the queue. */
+ private void writeVibrationReportedFromQueue() {
+ boolean needsScheduling;
+ VibrationStats.StatsInfo stats;
+
+ synchronized (mLock) {
+ stats = mVibrationStatsQueue.poll();
+ needsScheduling = !mVibrationStatsQueue.isEmpty();
+
+ if (stats != null) {
+ mLastVibrationReportedLogUptime = SystemClock.uptimeMillis();
+ }
+ }
+
+ if (stats == null) {
+ Slog.w(TAG, "Unexpected vibration metric flush with empty queue. Ignoring.");
+ } else {
+ stats.writeVibrationReported();
+ }
+
+ if (needsScheduling) {
+ mHandler.postDelayed(mConsumeVibrationStatsQueueRunnable,
+ mVibrationReportedLogIntervalMillis);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 5ac2f4f..2f12a82 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -129,6 +129,7 @@
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
+ private final VibratorFrameworkStatsLogger mFrameworkStatsLogger;
private final Handler mHandler;
private final VibrationThread mVibrationThread;
private final AppOpsManager mAppOps;
@@ -163,10 +164,12 @@
// When the system is entering a non-interactive state, we want to cancel
// vibrations in case a misbehaving app has abandoned them.
if (shouldCancelOnScreenOffLocked(mNextVibration)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ clearNextVibrationLocked(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF));
}
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
- mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SCREEN_OFF,
+ mCurrentVibration.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
/* immediate= */ false);
}
}
@@ -207,6 +210,7 @@
mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
mBatteryStatsService = injector.getBatteryStatsService();
+ mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -384,7 +388,8 @@
* The Vibration is only returned if it is ongoing after this method returns.
*/
@Nullable
- private Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
+ @VisibleForTesting
+ Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
@@ -399,6 +404,7 @@
return null;
}
attrs = fixupVibrationAttributes(attrs, effect);
+ // Create Vibration.Stats as close to the received request as possible, for tracking.
Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
uid, opPkg, reason);
fillVibrationFallbacks(vib, effect);
@@ -413,32 +419,56 @@
if (DEBUG) {
Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
}
- Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.uid, vib.opPkg, vib.attrs);
+ int ignoredByUid = -1;
+ int ignoredByUsage = -1;
+ Vibration.Status status = null;
- if (ignoreStatus == null) {
- ignoreStatus = shouldIgnoreVibrationForOngoingLocked(vib);
+ // Check if user settings or DnD is set to ignore this vibration.
+ status = shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs);
+
+ // Check if something has external control, assume it's more important.
+ if ((status == null) && (mCurrentExternalVibration != null)) {
+ status = Vibration.Status.IGNORED_FOR_EXTERNAL;
+ ignoredByUid = mCurrentExternalVibration.externalVibration.getUid();
+ ignoredByUsage = mCurrentExternalVibration.externalVibration
+ .getVibrationAttributes().getUsage();
}
- if (ignoreStatus != null) {
- endVibrationLocked(vib, ignoreStatus);
- return vib;
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mCurrentVibration != null) {
- mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
- /* immediate= */ false);
+ // Check if ongoing vibration is more important than this vibration.
+ if (status == null) {
+ status = shouldIgnoreVibrationForOngoingLocked(vib);
+ if (status != null) {
+ ignoredByUid = mCurrentVibration.getVibration().uid;
+ ignoredByUsage = mCurrentVibration.getVibration().attrs.getUsage();
}
- Vibration.Status status = startVibrationLocked(vib);
- if (status != Vibration.Status.RUNNING) {
- endVibrationLocked(vib, status);
- }
- return vib;
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+
+ // If not ignored so far then try to start this vibration.
+ if (status == null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mCurrentVibration != null) {
+ vib.stats().reportInterruptedAnotherVibration(
+ mCurrentVibration.getVibration().attrs.getUsage());
+ mCurrentVibration.notifyCancelled(
+ new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_SUPERSEDED, vib.uid,
+ vib.attrs.getUsage()),
+ /* immediate= */ false);
+ }
+ status = startVibrationLocked(vib);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Ignored or failed to start the vibration, end it and report metrics right away.
+ if (status != Vibration.Status.RUNNING) {
+ endVibrationLocked(vib,
+ new Vibration.EndInfo(status, ignoredByUid, ignoredByUsage),
+ /* shouldWriteStats= */ true);
+ }
+ return vib;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -457,26 +487,28 @@
if (DEBUG) {
Slog.d(TAG, "Canceling vibration");
}
+ Vibration.EndInfo cancelledByUserInfo =
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER);
final long ident = Binder.clearCallingIdentity();
try {
if (mNextVibration != null
&& shouldCancelVibration(mNextVibration.getVibration(),
usageFilter, token)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_USER);
+ clearNextVibrationLocked(cancelledByUserInfo);
}
if (mCurrentVibration != null
&& shouldCancelVibration(mCurrentVibration.getVibration(),
usageFilter, token)) {
- mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_USER,
- /* immediate= */false);
+ mCurrentVibration.notifyCancelled(
+ cancelledByUserInfo, /* immediate= */false);
}
if (mCurrentExternalVibration != null
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
- mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED_BY_USER,
- /* continueExternalControl= */ false);
+ mCurrentExternalVibration.mute();
+ endExternalVibrateLocked(
+ cancelledByUserInfo, /* continueExternalControl= */ false);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -604,15 +636,17 @@
Slog.d(TAG, "Canceling vibration because settings changed: "
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
}
- mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE,
+ mCurrentVibration.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
/* immediate= */ false);
}
}
}
- private void setExternalControl(boolean externalControl) {
+ private void setExternalControl(boolean externalControl, VibrationStats vibrationStats) {
for (int i = 0; i < mVibrators.size(); i++) {
mVibrators.valueAt(i).setExternalControl(externalControl);
+ vibrationStats.reportSetExternalControl();
}
}
@@ -654,7 +688,9 @@
}
// If there's already a vibration queued (waiting for the previous one to finish
// cancelling), end it cleanly and replace it with the new one.
- clearNextVibrationLocked(Vibration.Status.IGNORED_SUPERSEDED);
+ clearNextVibrationLocked(
+ new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED,
+ vib.uid, vib.attrs.getUsage()));
mNextVibration = conductor;
return Vibration.Status.RUNNING;
} finally {
@@ -671,6 +707,7 @@
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ // Make sure mCurrentVibration is set while triggering the VibrationThread.
mCurrentVibration = conductor;
if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) {
// Shouldn't happen. The method call already logs a wtf.
@@ -690,18 +727,26 @@
}
@GuardedBy("mLock")
- private void endVibrationLocked(Vibration vib, Vibration.Status status) {
- vib.end(status);
- logVibrationStatus(vib.uid, vib.attrs, status);
+ private void endVibrationLocked(Vibration vib, Vibration.EndInfo vibrationEndInfo,
+ boolean shouldWriteStats) {
+ vib.end(vibrationEndInfo);
+ logVibrationStatus(vib.uid, vib.attrs, vibrationEndInfo.status);
mVibratorManagerRecords.record(vib);
+ if (shouldWriteStats) {
+ mFrameworkStatsLogger.writeVibrationReportedAsync(
+ vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
+ }
}
@GuardedBy("mLock")
- private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
- vib.end(status);
+ private void endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib,
+ Vibration.EndInfo vibrationEndInfo) {
+ vib.end(vibrationEndInfo);
logVibrationStatus(vib.externalVibration.getUid(),
- vib.externalVibration.getVibrationAttributes(), status);
+ vib.externalVibration.getVibrationAttributes(), vibrationEndInfo.status);
mVibratorManagerRecords.record(vib);
+ mFrameworkStatsLogger.writeVibrationReportedAsync(
+ vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
}
private void logVibrationStatus(int uid, VibrationAttributes attrs, Vibration.Status status) {
@@ -744,15 +789,17 @@
}
@GuardedBy("mLock")
- private void reportFinishedVibrationLocked(Vibration.Status status) {
+ private void reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
try {
Vibration vib = mCurrentVibration.getVibration();
if (DEBUG) {
- Slog.d(TAG, "Reporting vibration " + vib.id + " finished with status " + status);
+ Slog.d(TAG, "Reporting vibration " + vib.id + " finished with " + vibrationEndInfo);
}
- endVibrationLocked(vib, status);
+ // DO NOT write metrics at this point, wait for the VibrationThread to report the
+ // vibration was released, after all cleanup. The metrics will be reported then.
+ endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ false);
finishAppOpModeLocked(vib.uid, vib.opPkg);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -791,11 +838,6 @@
@GuardedBy("mLock")
@Nullable
private Vibration.Status shouldIgnoreVibrationForOngoingLocked(Vibration vib) {
- if (mCurrentExternalVibration != null) {
- // If something has external control of the vibrator, assume that it's more important.
- return Vibration.Status.IGNORED_FOR_EXTERNAL;
- }
-
if (mCurrentVibration == null || vib.isRepeating()) {
// Incoming repeating vibrations always take precedence over ongoing vibrations.
return null;
@@ -1122,7 +1164,7 @@
}
Vibration vib = conductor.getVibration();
return mVibrationSettings.shouldCancelVibrationOnScreenOff(
- vib.uid, vib.opPkg, vib.attrs.getUsage(), vib.startUptimeMillis);
+ vib.uid, vib.opPkg, vib.attrs.getUsage(), vib.stats().getCreateUptimeMillis());
}
@GuardedBy("mLock")
@@ -1158,6 +1200,10 @@
BatteryStats.SERVICE_NAME));
}
+ VibratorFrameworkStatsLogger getFrameworkStatsLogger(Handler handler) {
+ return new VibratorFrameworkStatsLogger(handler);
+ }
+
VibratorController createVibratorController(int vibratorId,
VibratorController.OnVibrationCompleteListener listener) {
return new VibratorController(vibratorId, listener);
@@ -1197,6 +1243,10 @@
public void noteVibratorOn(int uid, long duration) {
try {
if (duration <= 0) {
+ // Tried to turn vibrator ON and got:
+ // duration == 0: Unsupported effect/method or zero-amplitude segment.
+ // duration < 0: Unexpected error triggering the vibrator.
+ // Skip battery stats and atom metric for VibratorStageChanged to ON.
return;
}
if (duration == Long.MAX_VALUE) {
@@ -1205,10 +1255,9 @@
duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
}
mBatteryStatsService.noteVibratorOn(uid, duration);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
- uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
- duration);
+ mFrameworkStatsLogger.writeVibratorStateOnAsync(uid, duration);
} catch (RemoteException e) {
+ Slog.e(TAG, "Error logging VibratorStateChanged to ON", e);
}
}
@@ -1216,22 +1265,21 @@
public void noteVibratorOff(int uid) {
try {
mBatteryStatsService.noteVibratorOff(uid);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
- uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
- /* duration= */ 0);
+ mFrameworkStatsLogger.writeVibratorStateOffAsync(uid);
} catch (RemoteException e) {
+ Slog.e(TAG, "Error logging VibratorStateChanged to OFF", e);
}
}
@Override
- public void onVibrationCompleted(long vibrationId, Vibration.Status status) {
+ public void onVibrationCompleted(long vibrationId, Vibration.EndInfo vibrationEndInfo) {
if (DEBUG) {
- Slog.d(TAG, "Vibration " + vibrationId + " finished with status " + status);
+ Slog.d(TAG, "Vibration " + vibrationId + " finished with " + vibrationEndInfo);
}
synchronized (mLock) {
if (mCurrentVibration != null
&& mCurrentVibration.getVibration().id == vibrationId) {
- reportFinishedVibrationLocked(status);
+ reportFinishedVibrationLocked(vibrationEndInfo);
}
}
}
@@ -1251,13 +1299,21 @@
"VibrationId mismatch on release. expected=%d, released=%d",
mCurrentVibration.getVibration().id, vibrationId));
}
- mCurrentVibration = null;
+ if (mCurrentVibration != null) {
+ // This is when we consider the current vibration complete, so report metrics.
+ mFrameworkStatsLogger.writeVibrationReportedAsync(
+ mCurrentVibration.getVibration().getStatsInfo(
+ /* completionUptimeMillis= */ SystemClock.uptimeMillis()));
+ mCurrentVibration = null;
+ }
if (mNextVibration != null) {
VibrationStepConductor nextConductor = mNextVibration;
mNextVibration = null;
Vibration.Status status = startVibrationOnThreadLocked(nextConductor);
if (status != Vibration.Status.RUNNING) {
- endVibrationLocked(nextConductor.getVibration(), status);
+ // Failed to start the vibration, end it and report metrics right away.
+ endVibrationLocked(nextConductor.getVibration(),
+ new Vibration.EndInfo(status), /* shouldWriteStats= */ true);
}
}
}
@@ -1325,31 +1381,48 @@
private final class ExternalVibrationHolder implements IBinder.DeathRecipient {
public final ExternalVibration externalVibration;
+ public final VibrationStats stats = new VibrationStats();
public int scale;
- private final long mStartUptimeMillis;
- private final long mStartTimeDebug;
-
- private long mEndUptimeMillis;
- private long mEndTimeDebug;
private Vibration.Status mStatus;
private ExternalVibrationHolder(ExternalVibration externalVibration) {
this.externalVibration = externalVibration;
this.scale = IExternalVibratorService.SCALE_NONE;
- mStartUptimeMillis = SystemClock.uptimeMillis();
- mStartTimeDebug = System.currentTimeMillis();
mStatus = Vibration.Status.RUNNING;
}
- public void end(Vibration.Status status) {
+ public void mute() {
+ externalVibration.mute();
+ }
+
+ public void linkToDeath() {
+ externalVibration.linkToDeath(this);
+ }
+
+ public void unlinkToDeath() {
+ externalVibration.unlinkToDeath(this);
+ }
+
+ public boolean isHoldingSameVibration(ExternalVibration externalVibration) {
+ return this.externalVibration.equals(externalVibration);
+ }
+
+ public void end(Vibration.EndInfo info) {
if (mStatus != Vibration.Status.RUNNING) {
- // Vibration already ended, keep first ending status set and ignore this one.
+ // Already ended, ignore this call
return;
}
- mStatus = status;
- mEndUptimeMillis = SystemClock.uptimeMillis();
- mEndTimeDebug = System.currentTimeMillis();
+ mStatus = info.status;
+ stats.reportEnded(info.endedByUid, info.endedByUsage);
+
+ if (stats.hasStarted()) {
+ // External vibration doesn't have feedback from total time the vibrator was playing
+ // with non-zero amplitude, so we use the duration between start and end times of
+ // the vibration as the time the vibrator was ON, since the haptic channels are
+ // open for this duration and can receive vibration waveform data.
+ stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
+ }
}
public void binderDied() {
@@ -1358,19 +1431,26 @@
if (DEBUG) {
Slog.d(TAG, "External vibration finished because binder died");
}
- endExternalVibrateLocked(Vibration.Status.CANCELLED_BINDER_DIED,
+ endExternalVibrateLocked(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
/* continueExternalControl= */ false);
}
}
}
public Vibration.DebugInfo getDebugInfo() {
- long durationMs = mEndUptimeMillis == 0 ? -1 : mEndUptimeMillis - mStartUptimeMillis;
return new Vibration.DebugInfo(
- mStartTimeDebug, mEndTimeDebug, durationMs,
- /* effect= */ null, /* originalEffect= */ null, scale,
+ mStatus, stats, /* effect= */ null, /* originalEffect= */ null, scale,
externalVibration.getVibrationAttributes(), externalVibration.getUid(),
- externalVibration.getPackage(), /* reason= */ null, mStatus);
+ externalVibration.getPackage(), /* reason= */ null);
+ }
+
+ public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
+ return new VibrationStats.StatsInfo(
+ externalVibration.getUid(),
+ FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
+ externalVibration.getVibrationAttributes().getUsage(), mStatus, stats,
+ completionUptimeMillis);
}
}
@@ -1500,9 +1580,11 @@
/** Clears mNextVibration if set, ending it cleanly */
@GuardedBy("mLock")
- private void clearNextVibrationLocked(Vibration.Status endStatus) {
+ private void clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
if (mNextVibration != null) {
- endVibrationLocked(mNextVibration.getVibration(), endStatus);
+ // Clearing next vibration before playing it, end it and report metrics right away.
+ endVibrationLocked(mNextVibration.getVibration(), vibrationEndInfo,
+ /* shouldWriteStats= */ true);
mNextVibration = null;
}
}
@@ -1510,25 +1592,25 @@
/**
* Ends the external vibration, and clears related service state.
*
- * @param status the status to end the associated Vibration with
+ * @param vibrationEndInfo the status and related info to end the associated Vibration with
* @param continueExternalControl indicates whether external control will continue. If not, the
* HAL will have external control turned off.
*/
@GuardedBy("mLock")
- private void endExternalVibrateLocked(Vibration.Status status,
+ private void endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo,
boolean continueExternalControl) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "endExternalVibrateLocked");
try {
if (mCurrentExternalVibration == null) {
return;
}
- endVibrationLocked(mCurrentExternalVibration, status);
- mCurrentExternalVibration.externalVibration.unlinkToDeath(
- mCurrentExternalVibration);
- mCurrentExternalVibration = null;
+ mCurrentExternalVibration.unlinkToDeath();
if (!continueExternalControl) {
- setExternalControl(false);
+ setExternalControl(false, mCurrentExternalVibration.stats);
}
+ // The external control was turned off, end it and report metrics right away.
+ endVibrationAndWriteStatsLocked(mCurrentExternalVibration, vibrationEndInfo);
+ mCurrentExternalVibration = null;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -1552,6 +1634,8 @@
return IExternalVibratorService.SCALE_MUTE;
}
+ // Create Vibration.Stats as close to the received request as possible, for tracking.
+ ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
VibrationAttributes attrs = fixupVibrationAttributes(vib.getVibrationAttributes(),
/* effect= */ null);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -1562,18 +1646,17 @@
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
- int scale;
synchronized (mLock) {
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
vib.getUid(), vib.getPackage(), attrs);
if (ignoreStatus != null) {
- ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
- endVibrationLocked(vibHolder, ignoreStatus);
+ // Failed to start the vibration, end it and report metrics right away.
+ endVibrationAndWriteStatsLocked(vibHolder, new Vibration.EndInfo(ignoreStatus));
return vibHolder.scale;
}
if (mCurrentExternalVibration != null
- && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ && mCurrentExternalVibration.isHoldingSameVibration(vib)) {
// We are already playing this external vibration, so we can return the same
// scale calculated in the previous call to this method.
return mCurrentExternalVibration.scale;
@@ -1582,8 +1665,14 @@
// If we're not under external control right now, then cancel any normal
// vibration that may be playing and ready the vibrator for external control.
if (mCurrentVibration != null) {
- clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
- mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ vibHolder.stats.reportInterruptedAnotherVibration(
+ mCurrentVibration.getVibration().attrs.getUsage());
+ clearNextVibrationLocked(
+ new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL,
+ vib.getUid(), attrs.getUsage()));
+ mCurrentVibration.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ vib.getUid(), attrs.getUsage()),
/* immediate= */ true);
waitForCompletion = true;
}
@@ -1597,22 +1686,27 @@
// Note that this doesn't support multiple concurrent external controls, as we
// would need to mute the old one still if it came from a different controller.
alreadyUnderExternalControl = true;
- mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED_SUPERSEDED,
+ mCurrentExternalVibration.mute();
+ vibHolder.stats.reportInterruptedAnotherVibration(
+ mCurrentExternalVibration.externalVibration
+ .getVibrationAttributes().getUsage());
+ endExternalVibrateLocked(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ vib.getUid(), attrs.getUsage()),
/* continueExternalControl= */ true);
}
- mCurrentExternalVibration = new ExternalVibrationHolder(vib);
- vib.linkToDeath(mCurrentExternalVibration);
- mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
- attrs.getUsage());
- scale = mCurrentExternalVibration.scale;
+ mCurrentExternalVibration = vibHolder;
+ vibHolder.linkToDeath();
+ vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage());
}
if (waitForCompletion) {
if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) {
Slog.e(TAG, "Timed out waiting for vibration to cancel");
synchronized (mLock) {
- endExternalVibrateLocked(Vibration.Status.IGNORED_ERROR_CANCELLING,
+ // Trigger endExternalVibrateLocked to unlink to death recipient.
+ endExternalVibrateLocked(
+ new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
/* continueExternalControl= */ false);
}
return IExternalVibratorService.SCALE_MUTE;
@@ -1622,23 +1716,27 @@
if (DEBUG) {
Slog.d(TAG, "Vibrator going under external control.");
}
- setExternalControl(true);
+ setExternalControl(true, vibHolder.stats);
}
if (DEBUG) {
Slog.e(TAG, "Playing external vibration: " + vib);
}
- return scale;
+ // Vibrator will start receiving data from external channels after this point.
+ // Report current time as the vibration start time, for debugging.
+ vibHolder.stats.reportStarted();
+ return vibHolder.scale;
}
@Override
public void onExternalVibrationStop(ExternalVibration vib) {
synchronized (mLock) {
if (mCurrentExternalVibration != null
- && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ && mCurrentExternalVibration.isHoldingSameVibration(vib)) {
if (DEBUG) {
Slog.e(TAG, "Stopping external vibration" + vib);
}
- endExternalVibrateLocked(Vibration.Status.FINISHED,
+ endExternalVibrateLocked(
+ new Vibration.EndInfo(Vibration.Status.FINISHED),
/* continueExternalControl= */ false);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3a2c9c8..208450d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5649,7 +5649,7 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppResumed: wasStopped=%b %s",
wasStopped, this);
mAppStopped = false;
- // Allow the window to turn the screen on once the app is resumed again.
+ // Allow the window to turn the screen on once the app is started and resumed.
if (mAtmService.getActivityStartController().isInExecution()) {
setCurrentLaunchCanTurnScreenOn(true);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 9be9340..7c25b53 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -94,6 +94,7 @@
boolean mCheckedForSetup = false;
+ /** Whether an {@link ActivityStarter} is currently executing (starting an Activity). */
private boolean mInExecution = false;
/**
@@ -129,7 +130,7 @@
return mFactory.obtain().setIntent(intent).setReason(reason);
}
- void onExecutionStarted(ActivityStarter starter) {
+ void onExecutionStarted() {
mInExecution = true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 99b34c7..f9e59c8 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -131,6 +131,7 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.power.ShutdownCheckPoints;
@@ -1268,7 +1269,7 @@
}
private void onExecutionStarted() {
- mController.onExecutionStarted(this);
+ mController.onExecutionStarted();
}
private boolean isHomeApp(int uid, @Nullable String packageName) {
@@ -2100,6 +2101,49 @@
}
}
+ // Log activity starts which violate one of the following rules of the
+ // activity security model (ASM):
+ // 1. Only the top activity on a task can start activities on that task
+ // 2. Only the top activity on the top task can create new (top) tasks
+ // We don't currently block, but these checks may later become blocks
+ // TODO(b/236234252): Shift to BackgroundActivityStartController once
+ // class is ready
+ if (mSourceRecord != null) {
+ int callerUid = mSourceRecord.getUid();
+ ActivityRecord targetTopActivity =
+ targetTask != null ? targetTask.getTopNonFinishingActivity() : null;
+ boolean passesAsmChecks = newTask
+ ? mService.mVisibleActivityProcessTracker.hasResumedActivity(callerUid)
+ : targetTopActivity != null && targetTopActivity.getUid() == callerUid;
+
+ if (!passesAsmChecks) {
+ Slog.i(TAG, "Launching r: " + r
+ + " from background: " + mSourceRecord
+ + ". New task: " + newTask);
+ boolean newOrEmptyTask = newTask || (targetTopActivity == null);
+ FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
+ /* caller_uid */
+ callerUid,
+ /* caller_activity_class_name */
+ mSourceRecord.info.name,
+ /* target_task_top_activity_uid */
+ newOrEmptyTask ? -1 : targetTopActivity.getUid(),
+ /* target_task_top_activity_class_name */
+ newOrEmptyTask ? null : targetTopActivity.info.name,
+ /* target_task_is_different */
+ newTask || !mSourceRecord.getTask().equals(targetTask),
+ /* target_activity_uid */
+ r.getUid(),
+ /* target_activity_class_name */
+ r.info.name,
+ /* target_intent_action */
+ r.intent.getAction(),
+ /* target_intent_flags */
+ r.intent.getFlags()
+ );
+ }
+ }
+
return START_SUCCESS;
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index fa3fcd9..235849c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -127,9 +127,17 @@
}
@Override
- public long compose(PrimitiveSegment[] effects, long vibrationId) {
+ public long compose(PrimitiveSegment[] primitives, long vibrationId) {
+ if (mSupportedPrimitives == null) {
+ return 0;
+ }
+ for (PrimitiveSegment primitive : primitives) {
+ if (Arrays.binarySearch(mSupportedPrimitives, primitive.getPrimitiveId()) < 0) {
+ return 0;
+ }
+ }
long duration = 0;
- for (PrimitiveSegment primitive : effects) {
+ for (PrimitiveSegment primitive : primitives) {
duration += EFFECT_DURATION + primitive.getDelay();
recordEffectSegment(vibrationId, primitive);
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationTest.java
new file mode 100644
index 0000000..b469299
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.stream.Collectors.toList;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link Vibration}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:VibrationTest
+ */
+@Presubmit
+public class VibrationTest {
+
+ @Test
+ public void status_hasUniqueProtoEnumValues() {
+ assertThat(
+ Arrays.stream(Vibration.Status.values())
+ .map(Vibration.Status::getProtoEnumValue)
+ .collect(toList()))
+ .containsNoDuplicates();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index de5f6ed..ca162ef 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -257,13 +257,18 @@
assertTrue(mThread.isRunningVibrationId(vibrationId));
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
- conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
+ Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_SUPERSEDED, /* endedByUid= */ 1,
+ /* endedByUsage= */ VibrationAttributes.USAGE_ALARM);
+ conductor.notifyCancelled(
+ cancelVibrationInfo,
+ /* immediate= */ false);
waitForCompletion();
assertFalse(mThread.isRunningVibrationId(vibrationId));
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
+ verifyCallbacksTriggered(vibrationId, cancelVibrationInfo);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
@@ -288,7 +293,9 @@
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ conductor.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
@@ -319,7 +326,9 @@
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ conductor.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false);
waitForCompletion();
// PWLE size max was used to generate a single vibrate call with 10 segments.
@@ -348,11 +357,13 @@
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
+ conductor.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ /* immediate= */ false);
waitForCompletion();
// Composition size max was used to generate a single vibrate call with 10 primitives.
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
}
@@ -370,7 +381,9 @@
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ conductor.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
@@ -394,7 +407,9 @@
assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
5000 + TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ conductor.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
@@ -414,6 +429,8 @@
public void vibrate_singleVibratorPredefinedCancel_cancelsVibrationImmediately()
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
@@ -431,7 +448,9 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
new Thread(() -> conductor.notifyCancelled(
- Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE, /* immediate= */ false));
+ new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+ /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
@@ -458,7 +477,9 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
new Thread(() -> conductor.notifyCancelled(
- Vibration.Status.CANCELLED_BY_SCREEN_OFF, /* immediate= */ false));
+ new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
@@ -519,7 +540,7 @@
startThreadAndDispatcher(vibrationId, effect);
waitForCompletion();
- verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
@@ -530,6 +551,8 @@
public void vibrate_singleVibratorComposed_runsVibration() throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
@@ -559,7 +582,7 @@
startThreadAndDispatcher(vibrationId, effect);
waitForCompletion();
- verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
+ verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
@@ -570,6 +593,10 @@
public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK,
+ VibrationEffect.Composition.PRIMITIVE_SPIN);
fakeVibrator.setCompositionSizeMax(2);
long vibrationId = 1;
@@ -809,6 +836,8 @@
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(4).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
long vibrationId = 1;
VibrationEffect composed = VibrationEffect.startComposition()
@@ -854,6 +883,8 @@
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
long vibrationId = 1;
@@ -902,7 +933,11 @@
long vibrationId = 1;
mockVibrators(vibratorIds);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
@@ -939,6 +974,8 @@
mockVibrators(vibratorIds);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(4).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(true);
@@ -1125,7 +1162,9 @@
// fail at waitForCompletion(cancellingThread).
Thread cancellingThread = new Thread(
() -> conductor.notifyCancelled(
- Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false));
+ new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false));
cancellingThread.start();
// Cancelling the vibration should be fast and return right away, even if the thread is
@@ -1143,6 +1182,8 @@
mockVibrators(1, 2);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
long vibrationId = 1;
CombinedVibration effect = CombinedVibration.startParallel()
@@ -1163,13 +1204,15 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread = new Thread(
() -> conductor.notifyCancelled(
- Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false));
+ new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1195,9 +1238,11 @@
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
- Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(
- Vibration.Status.CANCELLED_BY_SCREEN_OFF, /* immediate= */ false));
+ Thread cancellingThread = new Thread(
+ () -> conductor.notifyCancelled(
+ new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
@@ -1266,7 +1311,7 @@
// Vibration completed but vibrator not yet released.
verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
- eq(Vibration.Status.FINISHED));
+ eq(new Vibration.EndInfo(Vibration.Status.FINISHED)));
verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong());
// Thread still running ramp down.
@@ -1278,12 +1323,13 @@
// Will stop the ramp down right away.
conductor.notifyCancelled(
- Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE, /* immediate= */ true);
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+ /* immediate= */ true);
waitForCompletion();
// Does not cancel already finished vibration, but releases vibrator.
verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
- eq(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE));
+ eq(new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE)));
verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
}
@@ -1299,7 +1345,9 @@
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ conductor.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
@@ -1422,7 +1470,9 @@
VibrationStepConductor conductor2 = startThreadAndDispatcher(vibrationId2, effect2);
// Effect2 won't complete on its own. Cancel it after a couple of repeats.
Thread.sleep(150); // More than two TICKs.
- conductor2.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
+ conductor2.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
+ /* immediate= */ false);
waitForCompletion();
startThreadAndDispatcher(vibrationId3, effect3);
@@ -1431,7 +1481,9 @@
// Effect4 is a long oneshot, but it gets cancelled as fast as possible.
long start4 = System.currentTimeMillis();
VibrationStepConductor conductor4 = startThreadAndDispatcher(vibrationId4, effect4);
- conductor4.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ true);
+ conductor4.notifyCancelled(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ /* immediate= */ true);
waitForCompletion();
long duration4 = System.currentTimeMillis() - start4;
@@ -1469,7 +1521,7 @@
fakeVibrator.getEffectSegments(vibrationId3));
// Effect4: cancelled quickly.
- verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_SUPERSEDED);
+ verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertTrue("Tested duration=" + duration4, duration4 < 2000);
// Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have
@@ -1580,7 +1632,11 @@
}
private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
- verify(mManagerHooks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
+ verifyCallbacksTriggered(vibrationId, new Vibration.EndInfo(expectedStatus));
+ }
+
+ private void verifyCallbacksTriggered(long vibrationId, Vibration.EndInfo expectedEndInfo) {
+ verify(mManagerHooks).onVibrationCompleted(eq(vibrationId), eq(expectedEndInfo));
verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
new file mode 100644
index 0000000..c1ab1db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.server.vibrator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Tests for {@link VibratorFrameworkStatsLogger}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:VibratorFrameworkStatsLoggerTest
+ */
+@Presubmit
+public class VibratorFrameworkStatsLoggerTest {
+
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ private TestLooper mTestLooper;
+ private VibratorFrameworkStatsLogger mLogger;
+
+ @Before
+ public void setUp() {
+ mTestLooper = new TestLooper();
+ }
+
+ @Test
+ public void writeVibrationReportedAsync_afterMinInterval_writesRightAway() {
+ setUpLogger(/* minIntervalMillis= */ 10, /* queueMaxSize= */ 10);
+
+ VibrationStats.StatsInfo firstStats = newEmptyStatsInfo();
+ assertFalse(firstStats.isWritten());
+
+ mLogger.writeVibrationReportedAsync(firstStats);
+ mTestLooper.dispatchAll();
+ assertTrue(firstStats.isWritten());
+ }
+
+ @Test
+ public void writeVibrationReportedAsync_rightAfterLogging_schedulesToRunAfterRemainingDelay() {
+ setUpLogger(/* minIntervalMillis= */ 100, /* queueMaxSize= */ 10);
+
+ VibrationStats.StatsInfo firstStats = newEmptyStatsInfo();
+ VibrationStats.StatsInfo secondStats = newEmptyStatsInfo();
+ assertFalse(firstStats.isWritten());
+ assertFalse(secondStats.isWritten());
+
+ // Write first message at current SystemClock.uptimeMillis
+ mLogger.writeVibrationReportedAsync(firstStats);
+ mTestLooper.dispatchAll();
+ assertTrue(firstStats.isWritten());
+
+ // Second message is not written right away, it needs to wait the configured interval.
+ mLogger.writeVibrationReportedAsync(secondStats);
+ mTestLooper.dispatchAll();
+ assertFalse(secondStats.isWritten());
+
+ // Second message is written after delay passes.
+ mTestLooper.moveTimeForward(100);
+ mTestLooper.dispatchAll();
+ assertTrue(secondStats.isWritten());
+ }
+
+ @Test
+ public void writeVibrationReportedAsync_tooFast_logsUsingIntervalAndDropsMessagesFromQueue() {
+ setUpLogger(/* minIntervalMillis= */ 100, /* queueMaxSize= */ 2);
+
+ VibrationStats.StatsInfo firstStats = newEmptyStatsInfo();
+ VibrationStats.StatsInfo secondStats = newEmptyStatsInfo();
+ VibrationStats.StatsInfo thirdStats = newEmptyStatsInfo();
+
+ mLogger.writeVibrationReportedAsync(firstStats);
+ mLogger.writeVibrationReportedAsync(secondStats);
+ mLogger.writeVibrationReportedAsync(thirdStats);
+
+ // Only first message is logged.
+ mTestLooper.dispatchAll();
+ assertTrue(firstStats.isWritten());
+ assertFalse(secondStats.isWritten());
+ assertFalse(thirdStats.isWritten());
+
+ // Wait one interval to check only the second one is logged.
+ mTestLooper.moveTimeForward(100);
+ mTestLooper.dispatchAll();
+ assertTrue(secondStats.isWritten());
+ assertFalse(thirdStats.isWritten());
+
+ // Wait a long interval to check the third one was dropped and will never be logged.
+ mTestLooper.moveTimeForward(1_000);
+ mTestLooper.dispatchAll();
+ assertFalse(thirdStats.isWritten());
+ }
+
+ private void setUpLogger(int minIntervalMillis, int queueMaxSize) {
+ mLogger = new VibratorFrameworkStatsLogger(new Handler(mTestLooper.getLooper()),
+ minIntervalMillis, queueMaxSize);
+ }
+
+ private static VibrationStats.StatsInfo newEmptyStatsInfo() {
+ return new VibrationStats.StatsInfo(
+ 0, 0, 0, Vibration.Status.FINISHED, new VibrationStats(), 0L);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 8a96feb..36bec75 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -75,11 +76,13 @@
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
+import android.util.SparseBooleanArray;
import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
@@ -148,6 +151,8 @@
private IInputManager mIInputManagerMock;
@Mock
private IBatteryStats mBatteryStatsMock;
+ @Mock
+ private VibratorFrameworkStatsLogger mVibratorFrameworkStatsLoggerMock;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -233,6 +238,11 @@
}
@Override
+ VibratorFrameworkStatsLogger getFrameworkStatsLogger(Handler handler) {
+ return mVibratorFrameworkStatsLoggerMock;
+ }
+
+ @Override
VibratorController createVibratorController(int vibratorId,
VibratorController.OnVibrationCompleteListener listener) {
return mVibratorProviders.get(vibratorId)
@@ -806,11 +816,11 @@
service, TEST_TIMEOUT_MILLIS));
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
- new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
+ new long[]{10, 10}, new int[]{128, 255}, 1);
vibrate(service, repeatingEffect, NOTIFICATION_ATTRS);
// VibrationThread will start this vibration async, so wait before checking it started.
- assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
+ assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 2,
service, TEST_TIMEOUT_MILLIS));
// The second vibration should have recorded that the vibrators were turned on.
@@ -916,7 +926,11 @@
mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
// Mock alarm intensity equals to default value to avoid scaling in this test.
setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
@@ -1078,6 +1092,8 @@
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK);
VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.startComposition()
@@ -1380,6 +1396,373 @@
assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
}
+ @Test
+ public void frameworkStats_externalVibration_reportsAllMetrics() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ createSystemReadyService();
+
+ AudioAttributes audioAttrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM)
+ .build();
+
+ ExternalVibration vib = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
+ mock(IExternalVibrationController.class));
+ mExternalVibratorService.onExternalVibrationStart(vib);
+
+ Thread.sleep(10);
+ mExternalVibratorService.onExternalVibrationStop(vib);
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo statsInfo = argumentCaptor.getValue();
+ assertEquals(UID, statsInfo.uid);
+ assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
+ statsInfo.vibrationType);
+ assertEquals(VibrationAttributes.USAGE_ALARM, statsInfo.usage);
+ assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), statsInfo.status);
+ assertTrue(statsInfo.totalDurationMillis > 0);
+ assertTrue(
+ "Expected vibrator ON for at least 10ms, got " + statsInfo.vibratorOnMillis + "ms",
+ statsInfo.vibratorOnMillis >= 10);
+ assertEquals(2, statsInfo.halSetExternalControlCount);
+ }
+
+ @Test
+ public void frameworkStats_waveformVibration_reportsAllMetrics() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ VibratorManagerService service = createSystemReadyService();
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.createWaveform(new long[] {0, 10, 20, 10}, -1), RINGTONE_ATTRS);
+
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOnAsync(eq(UID), anyLong());
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOffAsync(eq(UID));
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo metrics = argumentCaptor.getValue();
+ assertEquals(UID, metrics.uid);
+ assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
+ metrics.vibrationType);
+ assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage);
+ assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+ assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
+ metrics.totalDurationMillis >= 20);
+ assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
+ metrics.vibratorOnMillis >= 20);
+
+ // All unrelated metrics are empty.
+ assertEquals(0, metrics.repeatCount);
+ assertEquals(0, metrics.halComposeCount);
+ assertEquals(0, metrics.halComposePwleCount);
+ assertEquals(0, metrics.halPerformCount);
+ assertEquals(0, metrics.halSetExternalControlCount);
+ assertEquals(0, metrics.halCompositionSize);
+ assertEquals(0, metrics.halPwleSize);
+ assertNull(metrics.halSupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halSupportedEffectsUsed);
+ assertNull(metrics.halUnsupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halUnsupportedEffectsUsed);
+
+ // Accommodate for ramping off config that might add extra setAmplitudes.
+ assertEquals(2, metrics.halOnCount);
+ assertTrue(metrics.halOffCount > 0);
+ assertTrue(metrics.halSetAmplitudeCount >= 2);
+ }
+
+ @Test
+ public void frameworkStats_repeatingVibration_reportsAllMetrics() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ VibratorManagerService service = createSystemReadyService();
+ vibrate(service, VibrationEffect.createWaveform(new long[] {10, 100}, 1), RINGTONE_ATTRS);
+
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOnAsync(eq(UID), anyLong());
+
+ // Wait for at least one loop before cancelling it.
+ Thread.sleep(100);
+ service.cancelVibrate(VibrationAttributes.USAGE_RINGTONE, service);
+
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOffAsync(eq(UID));
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo metrics = argumentCaptor.getValue();
+ assertEquals(UID, metrics.uid);
+ assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED,
+ metrics.vibrationType);
+ assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage);
+ assertEquals(Vibration.Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status);
+ assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
+ metrics.totalDurationMillis >= 100);
+ assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
+ metrics.vibratorOnMillis >= 100);
+
+ // All unrelated metrics are empty.
+ assertTrue(metrics.repeatCount > 0);
+ assertEquals(0, metrics.halComposeCount);
+ assertEquals(0, metrics.halComposePwleCount);
+ assertEquals(0, metrics.halPerformCount);
+ assertEquals(0, metrics.halSetExternalControlCount);
+ assertEquals(0, metrics.halCompositionSize);
+ assertEquals(0, metrics.halPwleSize);
+ assertNull(metrics.halSupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halSupportedEffectsUsed);
+ assertNull(metrics.halUnsupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halUnsupportedEffectsUsed);
+
+ // Accommodate for ramping off config that might add extra setAmplitudes.
+ assertTrue(metrics.halOnCount > 0);
+ assertTrue(metrics.halOffCount > 0);
+ assertTrue(metrics.halSetAmplitudeCount > 0);
+ }
+
+ @Test
+ public void frameworkStats_prebakedAndComposedVibrations_reportsAllMetrics() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(1).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_TICK);
+
+ VibratorManagerService service = createSystemReadyService();
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose(),
+ ALARM_ATTRS);
+
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOnAsync(eq(UID), anyLong());
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOffAsync(eq(UID));
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo metrics = argumentCaptor.getValue();
+ assertEquals(UID, metrics.uid);
+ assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
+ metrics.vibrationType);
+ assertEquals(VibrationAttributes.USAGE_ALARM, metrics.usage);
+ assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+
+ // At least 4 effect/primitive played, 20ms each, plus configured fallback.
+ assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
+ metrics.totalDurationMillis >= 80);
+ assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
+ metrics.vibratorOnMillis >= 80);
+
+ // Related metrics were collected.
+ assertEquals(2, metrics.halComposeCount); // TICK+TICK, then CLICK+CLICK
+ assertEquals(3, metrics.halPerformCount); // CLICK, TICK, then CLICK
+ assertEquals(4, metrics.halCompositionSize); // 2*TICK + 2*CLICK
+ // No repetitions in reported effect/primitive IDs.
+ assertArrayEquals(new int[] {VibrationEffect.Composition.PRIMITIVE_TICK},
+ metrics.halSupportedCompositionPrimitivesUsed);
+ assertArrayEquals(new int[] {VibrationEffect.Composition.PRIMITIVE_CLICK},
+ metrics.halUnsupportedCompositionPrimitivesUsed);
+ assertArrayEquals(new int[] {VibrationEffect.EFFECT_CLICK},
+ metrics.halSupportedEffectsUsed);
+ assertArrayEquals(new int[] {VibrationEffect.EFFECT_TICK},
+ metrics.halUnsupportedEffectsUsed);
+
+ // All unrelated metrics are empty.
+ assertEquals(0, metrics.repeatCount);
+ assertEquals(0, metrics.halComposePwleCount);
+ assertEquals(0, metrics.halSetExternalControlCount);
+ assertEquals(0, metrics.halPwleSize);
+
+ // Accommodate for ramping off config that might add extra setAmplitudes
+ // for the effect that plays the fallback instead of "perform".
+ assertTrue(metrics.halOnCount > 0);
+ assertTrue(metrics.halOffCount > 0);
+ assertTrue(metrics.halSetAmplitudeCount > 0);
+ }
+
+ @Test
+ public void frameworkStats_interruptingVibrations_reportsAllMetrics() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ vibrate(service, VibrationEffect.createOneShot(1_000, 128), HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
+ assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrateAndWaitUntilFinished(service, VibrationEffect.createOneShot(10, 255), ALARM_ATTRS);
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS).times(2))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0);
+ assertEquals(UID, touchMetrics.uid);
+ assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage);
+ assertEquals(Vibration.Status.CANCELLED_SUPERSEDED.getProtoEnumValue(),
+ touchMetrics.status);
+ assertTrue(touchMetrics.endedBySameUid);
+ assertEquals(VibrationAttributes.USAGE_ALARM, touchMetrics.endedByUsage);
+ assertEquals(-1, touchMetrics.interruptedUsage);
+
+ VibrationStats.StatsInfo alarmMetrics = argumentCaptor.getAllValues().get(1);
+ assertEquals(UID, alarmMetrics.uid);
+ assertEquals(VibrationAttributes.USAGE_ALARM, alarmMetrics.usage);
+ assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), alarmMetrics.status);
+ assertFalse(alarmMetrics.endedBySameUid);
+ assertEquals(-1, alarmMetrics.endedByUsage);
+ assertEquals(VibrationAttributes.USAGE_TOUCH, alarmMetrics.interruptedUsage);
+ }
+
+ @Test
+ public void frameworkStats_ignoredVibration_reportsStatus() throws Exception {
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ // Haptic feedback ignored in low power state
+ vibrateAndWaitUntilFinished(service, VibrationEffect.createOneShot(100, 128),
+ HAPTIC_FEEDBACK_ATTRS);
+ // Ringtone vibration user settings are off
+ vibrateAndWaitUntilFinished(service, VibrationEffect.createOneShot(200, 128),
+ RINGTONE_ATTRS);
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS).times(2))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0);
+ assertEquals(UID, touchMetrics.uid);
+ assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage);
+ assertEquals(Vibration.Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status);
+
+ VibrationStats.StatsInfo ringtoneMetrics = argumentCaptor.getAllValues().get(1);
+ assertEquals(UID, ringtoneMetrics.uid);
+ assertEquals(VibrationAttributes.USAGE_RINGTONE, ringtoneMetrics.usage);
+ assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(),
+ ringtoneMetrics.status);
+
+ for (VibrationStats.StatsInfo metrics : argumentCaptor.getAllValues()) {
+ // Latencies are empty since vibrations never started
+ assertEquals(0, metrics.startLatencyMillis);
+ assertEquals(0, metrics.endLatencyMillis);
+ assertEquals(0, metrics.vibratorOnMillis);
+
+ // All unrelated metrics are empty.
+ assertEquals(0, metrics.repeatCount);
+ assertEquals(0, metrics.halComposeCount);
+ assertEquals(0, metrics.halComposePwleCount);
+ assertEquals(0, metrics.halOffCount);
+ assertEquals(0, metrics.halOnCount);
+ assertEquals(0, metrics.halPerformCount);
+ assertEquals(0, metrics.halSetExternalControlCount);
+ assertEquals(0, metrics.halCompositionSize);
+ assertEquals(0, metrics.halPwleSize);
+ assertNull(metrics.halSupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halSupportedEffectsUsed);
+ assertNull(metrics.halUnsupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halUnsupportedEffectsUsed);
+ }
+ }
+
+ @Test
+ public void frameworkStats_multiVibrators_reportsAllMetrics() throws Exception {
+ mockVibrators(1, 2);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(1).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_TICK);
+ mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_TICK);
+
+ VibratorManagerService service = createSystemReadyService();
+ vibrateAndWaitUntilFinished(service,
+ CombinedVibration.startParallel()
+ .addVibrator(1,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .compose())
+ .addVibrator(2,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
+ .combine(),
+ NOTIFICATION_ATTRS);
+
+ SparseBooleanArray expectedEffectsUsed = new SparseBooleanArray();
+ expectedEffectsUsed.put(VibrationEffect.EFFECT_TICK, true);
+
+ SparseBooleanArray expectedPrimitivesUsed = new SparseBooleanArray();
+ expectedPrimitivesUsed.put(VibrationEffect.Composition.PRIMITIVE_TICK, true);
+
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOnAsync(eq(UID), anyLong());
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibratorStateOffAsync(eq(UID));
+
+ ArgumentCaptor<VibrationStats.StatsInfo> argumentCaptor =
+ ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibrationReportedAsync(argumentCaptor.capture());
+
+ VibrationStats.StatsInfo metrics = argumentCaptor.getValue();
+ assertEquals(UID, metrics.uid);
+ assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
+ metrics.vibrationType);
+ assertEquals(VibrationAttributes.USAGE_NOTIFICATION, metrics.usage);
+ assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+ assertTrue(metrics.totalDurationMillis >= 20);
+
+ // vibratorOnMillis accumulates both vibrators, it's 20 for each constant.
+ assertEquals(40, metrics.vibratorOnMillis);
+
+ // Related metrics were collected.
+ assertEquals(1, metrics.halComposeCount);
+ assertEquals(1, metrics.halPerformCount);
+ assertEquals(1, metrics.halCompositionSize);
+ assertEquals(2, metrics.halOffCount);
+ assertArrayEquals(new int[] {VibrationEffect.Composition.PRIMITIVE_TICK},
+ metrics.halSupportedCompositionPrimitivesUsed);
+ assertArrayEquals(new int[] {VibrationEffect.EFFECT_TICK},
+ metrics.halSupportedEffectsUsed);
+
+ // All unrelated metrics are empty.
+ assertEquals(0, metrics.repeatCount);
+ assertEquals(0, metrics.halComposePwleCount);
+ assertEquals(0, metrics.halOnCount);
+ assertEquals(0, metrics.halSetAmplitudeCount);
+ assertEquals(0, metrics.halSetExternalControlCount);
+ assertEquals(0, metrics.halPwleSize);
+ assertNull(metrics.halUnsupportedCompositionPrimitivesUsed);
+ assertNull(metrics.halUnsupportedEffectsUsed);
+ }
+
private VibrationEffectSegment expectedPrebaked(int effectId) {
return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
@@ -1429,6 +1812,20 @@
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
}
+ private void vibrateAndWaitUntilFinished(VibratorManagerService service, VibrationEffect effect,
+ VibrationAttributes attrs) throws InterruptedException {
+ vibrateAndWaitUntilFinished(service, CombinedVibration.createParallel(effect), attrs);
+ }
+
+ private void vibrateAndWaitUntilFinished(VibratorManagerService service,
+ CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
+ Vibration vib =
+ service.vibrateInternal(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
+ if (vib != null) {
+ vib.waitForEnd();
+ }
+ }
+
private void vibrate(VibratorManagerService service, VibrationEffect effect,
VibrationAttributes attrs) {
vibrate(service, CombinedVibration.createParallel(effect), attrs);