From 33c8a1a769b1e7b1a98301fd71a72db756f3a0e9 Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Fri, 27 Aug 2021 15:40:07 -0700 Subject: [PATCH 1/2] Update side fingerprint sensor overlay assets. The UI offsets are still missing so the alignment is not complete and some other parts of systemui can overlap in certain configurations. Bug: 197265282 Test: atest SidefpsControllerTest Test: manual (enroll & rotate device) Change-Id: Iaf7d8d2fd6e4204541857fabdd7c76999645c541 --- packages/SystemUI/res/layout/sidefps_view.xml | 16 +- packages/SystemUI/res/raw/sfps_pulse.json | 1 + .../res/raw/sfps_pulse_landscape.json | 1 + .../systemui/biometrics/AuthController.java | 12 +- .../biometrics/BiometricDisplayListener.kt | 89 +++++++ .../BiometricOrientationEventListener.kt | 57 ----- .../biometrics/SidefpsController.java | 239 ------------------ .../systemui/biometrics/SidefpsController.kt | 174 +++++++++++++ .../systemui/biometrics/SidefpsView.java | 107 -------- .../systemui/biometrics/UdfpsController.java | 19 +- .../biometrics/SidefpsControllerTest.kt | 58 ++++- .../aidl/FingerprintAuthenticationClient.java | 4 +- .../fingerprint/aidl/FingerprintProvider.java | 2 +- .../fingerprint/hidl/Fingerprint21.java | 3 +- .../hidl/FingerprintAuthenticationClient.java | 4 +- 15 files changed, 357 insertions(+), 429 deletions(-) create mode 100644 packages/SystemUI/res/raw/sfps_pulse.json create mode 100644 packages/SystemUI/res/raw/sfps_pulse_landscape.json create mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java create mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml index 397763531eb91..921f788309810 100644 --- a/packages/SystemUI/res/layout/sidefps_view.xml +++ b/packages/SystemUI/res/layout/sidefps_view.xml @@ -14,11 +14,13 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/sidefps_animation" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:lottie_autoPlay="true" + app:lottie_loop="true" + app:lottie_rawRes="@raw/sfps_pulse" + android:contentDescription="@string/accessibility_fingerprint_label"/> diff --git a/packages/SystemUI/res/raw/sfps_pulse.json b/packages/SystemUI/res/raw/sfps_pulse.json new file mode 100644 index 0000000000000..c4903a2857a16 --- /dev/null +++ b/packages/SystemUI/res/raw/sfps_pulse.json @@ -0,0 +1 @@ +{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"Fingerprint Pulse Motion","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[28,40,0],"to":[0.751,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,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],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,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.218,40],"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":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,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":[28.253,40],"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},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":270,"op":330,"st":270,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/packages/SystemUI/res/raw/sfps_pulse_landscape.json b/packages/SystemUI/res/raw/sfps_pulse_landscape.json new file mode 100644 index 0000000000000..8c91762d72860 --- /dev/null +++ b/packages/SystemUI/res/raw/sfps_pulse_landscape.json @@ -0,0 +1 @@ +{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":80,"h":42,"nm":"Fingerprint Pulse Motion Portrait","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[40,14,0],"to":[0,-0.751,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,-0.751,0]},{"t":300,"s":[40,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,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],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,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.218,40],"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":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,0.122,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,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":[28.253,40],"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},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,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":[16.338,0],"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":270,"op":330,"st":270,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 0790af94f287a..71445a7c2cfe5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -114,7 +114,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @VisibleForTesting IBiometricSysuiReceiver mReceiver; @VisibleForTesting - @NonNull final BiometricOrientationEventListener mOrientationListener; + @NonNull final BiometricDisplayListener mOrientationListener; @Nullable private final List mFaceProps; @Nullable private List mFpProps; @Nullable private List mUdfpsProps; @@ -459,13 +459,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mSidefpsControllerFactory = sidefpsControllerFactory; mWindowManager = windowManager; mUdfpsEnrolledForUser = new SparseBooleanArray(); - mOrientationListener = new BiometricOrientationEventListener(context, + mOrientationListener = new BiometricDisplayListener( + context, + displayManager, + handler, + BiometricDisplayListener.SensorType.Generic.INSTANCE, () -> { onOrientationChanged(); return Unit.INSTANCE; - }, - displayManager, - handler); + }); mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt new file mode 100644 index 0000000000000..b7404dfeb1cc3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics + +import android.content.Context +import android.hardware.display.DisplayManager +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal +import android.os.Handler +import android.view.Surface +import com.android.systemui.biometrics.BiometricDisplayListener.SensorType.Generic + +/** + * A listener for keeping overlays for biometric sensors aligned with the physical device + * device's screen. The [onChanged] will be dispatched on the [handler] + * whenever a relevant change to the device's configuration (orientation, fold, display change, + * etc.) may require the UI to change for the given [sensorType]. + */ +class BiometricDisplayListener( + private val context: Context, + private val displayManager: DisplayManager, + private val handler: Handler, + private val sensorType: SensorType = SensorType.Generic, + private val onChanged: () -> Unit +) : DisplayManager.DisplayListener { + + private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0 + + override fun onDisplayAdded(displayId: Int) {} + override fun onDisplayRemoved(displayId: Int) {} + override fun onDisplayChanged(displayId: Int) { + val rotationChanged = didRotationChange() + + when (sensorType) { + is SensorType.SideFingerprint -> onChanged() + else -> { + if (rotationChanged) { + onChanged() + } + } + } + } + + private fun didRotationChange(): Boolean { + val rotation = context.display?.rotation ?: return false + val last = lastRotation + lastRotation = rotation + return last != rotation + } + + /** Listen for changes. */ + fun enable() { + displayManager.registerDisplayListener(this, handler) + } + + /** Stop listening for changes. */ + fun disable() { + displayManager.unregisterDisplayListener(this) + } + + /** + * Type of sensor to determine what kind of display changes require layouts. + * + * The [Generic] type should be used in cases where the modality can vary, such as + * biometric prompt (and this object will likely change as multi-mode auth is added). + */ + sealed class SensorType { + object Generic : SensorType() + data class UnderDisplayFingerprint( + val properties: FingerprintSensorPropertiesInternal + ) : SensorType() + data class SideFingerprint( + val properties: FingerprintSensorPropertiesInternal + ) : SensorType() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt deleted file mode 100644 index 98a03a1c444b7..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.content.Context -import android.hardware.display.DisplayManager -import android.os.Handler -import android.view.OrientationEventListener -import android.view.Surface - -/** - * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever - * the orientation of the device has changed in order to keep overlays for biometric sensors - * aligned with the device's screen. - */ -class BiometricOrientationEventListener( - private val context: Context, - private val onOrientationChanged: () -> Unit, - private val displayManager: DisplayManager, - private val handler: Handler -) : DisplayManager.DisplayListener { - - private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0 - - override fun onDisplayAdded(displayId: Int) {} - override fun onDisplayRemoved(displayId: Int) {} - override fun onDisplayChanged(displayId: Int) { - val rotation = context.display?.rotation ?: return - if (lastRotation != rotation) { - lastRotation = rotation - - onOrientationChanged() - } - } - - fun enable() { - displayManager.registerDisplayListener(this, handler) - } - - fun disable() { - displayManager.unregisterDisplayListener(this) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java deleted file mode 100644 index 8f6e2498a00b5..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static com.android.internal.util.Preconditions.checkArgument; -import static com.android.internal.util.Preconditions.checkNotNull; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.PixelFormat; -import android.hardware.display.DisplayManager; -import android.hardware.fingerprint.FingerprintManager; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; -import android.hardware.fingerprint.ISidefpsController; -import android.os.Handler; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.Surface; -import android.view.WindowManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.R; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import javax.inject.Inject; - -import kotlin.Unit; - -/** - * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events. - */ -@SysUISingleton -public class SidefpsController { - private static final String TAG = "SidefpsController"; - @NonNull private final Context mContext; - @NonNull private final LayoutInflater mInflater; - private final FingerprintManager mFingerprintManager; - private final WindowManager mWindowManager; - private final DelayableExecutor mFgExecutor; - @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener; - - // TODO: update mDisplayHeight and mDisplayWidth for multi-display devices - private final int mDisplayHeight; - private final int mDisplayWidth; - - private boolean mIsVisible = false; - @Nullable private SidefpsView mView; - - static final int SFPS_AFFORDANCE_WIDTH = 50; // in default portrait mode - - @NonNull - private final ISidefpsController mSidefpsControllerImpl = new ISidefpsController.Stub() { - @Override - public void show() { - mFgExecutor.execute(() -> { - SidefpsController.this.show(); - mIsVisible = true; - }); - } - - @Override - public void hide() { - mFgExecutor.execute(() -> { - SidefpsController.this.hide(); - mIsVisible = false; - }); - } - }; - - @VisibleForTesting - final FingerprintSensorPropertiesInternal mSensorProps; - private final WindowManager.LayoutParams mCoreLayoutParams; - - @Inject - public SidefpsController(@NonNull Context context, - @NonNull LayoutInflater inflater, - @Nullable FingerprintManager fingerprintManager, - @NonNull WindowManager windowManager, - @Main DelayableExecutor fgExecutor, - @NonNull DisplayManager displayManager, - @Main Handler handler) { - mContext = context; - mInflater = inflater; - mFingerprintManager = checkNotNull(fingerprintManager); - mWindowManager = windowManager; - mFgExecutor = fgExecutor; - mOrientationListener = new BiometricOrientationEventListener( - context, - () -> { - onOrientationChanged(); - return Unit.INSTANCE; - }, - displayManager, - handler); - - mSensorProps = findFirstSidefps(); - checkArgument(mSensorProps != null); - - mCoreLayoutParams = new WindowManager.LayoutParams( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, - getCoreLayoutParamFlags(), - PixelFormat.TRANSLUCENT); - mCoreLayoutParams.setTitle(TAG); - // Overrides default, avoiding status bars during layout - mCoreLayoutParams.setFitInsetsTypes(0); - mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; - mCoreLayoutParams.layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; - - DisplayMetrics displayMetrics = new DisplayMetrics(); - windowManager.getDefaultDisplay().getMetrics(displayMetrics); - mDisplayHeight = displayMetrics.heightPixels; - mDisplayWidth = displayMetrics.widthPixels; - - mFingerprintManager.setSidefpsController(mSidefpsControllerImpl); - } - - private void show() { - mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false); - mView.setSensorProperties(mSensorProps); - mWindowManager.addView(mView, computeLayoutParams()); - - mOrientationListener.enable(); - } - - private void hide() { - if (mView != null) { - mWindowManager.removeView(mView); - mView.setOnTouchListener(null); - mView.setOnHoverListener(null); - mView = null; - } else { - Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); - } - - mOrientationListener.disable(); - } - - private void onOrientationChanged() { - // If mView is null or if view is hidden, then return. - if (mView == null || !mIsVisible) { - return; - } - - // If the overlay needs to be displayed with a new configuration, destroy the current - // overlay, and re-create and show the overlay with the updated LayoutParams. - hide(); - show(); - } - - @Nullable - private FingerprintSensorPropertiesInternal findFirstSidefps() { - for (FingerprintSensorPropertiesInternal props : - mFingerprintManager.getSensorPropertiesInternal()) { - if (props.isAnySidefpsType()) { - // TODO(b/188690214): L155-L173 can be removed once sensorLocationX, - // sensorLocationY, and sensorRadius are defined in sensorProps by the HAL - int sensorLocationX = 25; - int sensorLocationY = 610; - int sensorRadius = 112; - - FingerprintSensorPropertiesInternal tempProps = - new FingerprintSensorPropertiesInternal( - props.sensorId, - props.sensorStrength, - props.maxEnrollmentsPerUser, - props.componentInfo, - props.sensorType, - props.resetLockoutRequiresHardwareAuthToken, - sensorLocationX, - sensorLocationY, - sensorRadius - ); - props = tempProps; - return props; - } - } - return null; - } - - private int getCoreLayoutParamFlags() { - return WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; - } - - /** - * Computes layout params depending on orientation & folding configuration of device - */ - private WindowManager.LayoutParams computeLayoutParams() { - mCoreLayoutParams.flags = getCoreLayoutParamFlags(); - // Y value of top of affordance in portrait mode, X value of left of affordance in landscape - int sfpsLocationY = mSensorProps.sensorLocationY - mSensorProps.sensorRadius; - int sfpsAffordanceHeight = mSensorProps.sensorRadius * 2; - - // Calculate coordinates of drawable area for the fps affordance, accounting for orientation - switch (mContext.getDisplay().getRotation()) { - case Surface.ROTATION_90: - mCoreLayoutParams.x = sfpsLocationY; - mCoreLayoutParams.y = 0; - mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH; - mCoreLayoutParams.width = sfpsAffordanceHeight; - break; - case Surface.ROTATION_270: - mCoreLayoutParams.x = mDisplayHeight - sfpsLocationY - sfpsAffordanceHeight; - mCoreLayoutParams.y = mDisplayWidth - SFPS_AFFORDANCE_WIDTH; - mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH; - mCoreLayoutParams.width = sfpsAffordanceHeight; - break; - default: // Portrait - mCoreLayoutParams.x = mDisplayWidth - SFPS_AFFORDANCE_WIDTH; - mCoreLayoutParams.y = sfpsLocationY; - mCoreLayoutParams.height = sfpsAffordanceHeight; - mCoreLayoutParams.width = SFPS_AFFORDANCE_WIDTH; - } - return mCoreLayoutParams; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt new file mode 100644 index 0000000000000..54d5934d880d1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.biometrics + +import android.content.Context +import android.graphics.PixelFormat +import android.hardware.display.DisplayManager +import android.hardware.fingerprint.FingerprintManager +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal +import android.hardware.fingerprint.ISidefpsController +import android.os.Handler +import android.util.Log +import android.view.Display +import android.view.Gravity +import android.view.LayoutInflater +import android.view.Surface +import android.view.View +import android.view.WindowManager +import androidx.annotation.RawRes +import com.airbnb.lottie.LottieAnimationView +import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.util.concurrency.DelayableExecutor +import javax.inject.Inject + +private const val TAG = "SidefpsController" + +/** + * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events. + */ +@SysUISingleton +class SidefpsController @Inject constructor( + private val context: Context, + private val layoutInflater: LayoutInflater, + fingerprintManager: FingerprintManager?, + private val windowManager: WindowManager, + @Main mainExecutor: DelayableExecutor, + displayManager: DisplayManager, + @Main handler: Handler +) { + @VisibleForTesting + val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager + ?.sensorPropertiesInternal + ?.filter { it.isAnySidefpsType } + // TODO(b/188690214): remove - should directly come from HAL + ?.map { props -> + FingerprintSensorPropertiesInternal( + props.sensorId, + props.sensorStrength, + props.maxEnrollmentsPerUser, + props.componentInfo, + props.sensorType, + props.resetLockoutRequiresHardwareAuthToken, + 25 /* sensorLocationX */, + 610 /* sensorLocationY */, + 112 /* sensorRadius */ + ) + }?.firstOrNull() ?: throw IllegalStateException("no side fingerprint sensor") + + @VisibleForTesting + val orientationListener = BiometricDisplayListener( + context, + displayManager, + handler, + BiometricDisplayListener.SensorType.SideFingerprint(sensorProps) + ) { onOrientationChanged() } + + private var overlayView: View? = null + set(value) { + field?.let { oldView -> + windowManager.removeView(oldView) + orientationListener.disable() + } + field = value + field?.let { newView -> + windowManager.addView(newView, overlayViewParams) + orientationListener.enable() + } + } + + private val overlayViewParams = WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + PixelFormat.TRANSLUCENT + ).apply { + title = TAG + fitInsetsTypes = 0 // overrides default, avoiding status bars during layout + gravity = Gravity.TOP or Gravity.LEFT + layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY + } + + init { + fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() { + override fun show() = mainExecutor.execute { + if (overlayView == null) { + overlayView = createOverlayForDisplay() + } else { + Log.v(TAG, "overlay already shown") + } + } + override fun hide() = mainExecutor.execute { overlayView = null } + }) + } + + private fun onOrientationChanged() { + if (overlayView != null) { + overlayView = createOverlayForDisplay() + } + } + + private fun createOverlayForDisplay(): View { + val view = layoutInflater.inflate(R.layout.sidefps_view, null, false) + + val display = context.display!! + val isPortrait = display.isPortrait() + val size = windowManager.maximumWindowMetrics.bounds + val displayWidth = if (isPortrait) size.width() else size.height() + val displayHeight = if (isPortrait) size.height() else size.width() + + val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView + lottie.setAnimation(display.asSideFpsAnimation()) + view.rotation = display.asSideFpsAnimationRotation() + + // ignore sensorLocationX and sensorRadius since it's assumed to be on the side + // of the device and centered at sensorLocationY + val (x, y) = when (display.rotation) { + Surface.ROTATION_90 -> Pair(sensorProps.sensorLocationY, 0) + Surface.ROTATION_270 -> Pair(displayHeight - sensorProps.sensorLocationY, displayWidth) + Surface.ROTATION_180 -> Pair(0, displayHeight - sensorProps.sensorLocationY) + else -> Pair(displayWidth, sensorProps.sensorLocationY) + } + overlayViewParams.x = x + overlayViewParams.y = y + + return view + } +} + +@RawRes +private fun Display.asSideFpsAnimation(): Int = when (rotation) { + Surface.ROTATION_0 -> R.raw.sfps_pulse + Surface.ROTATION_180 -> R.raw.sfps_pulse + else -> R.raw.sfps_pulse_landscape +} + +private fun Display.asSideFpsAnimationRotation(): Float = when (rotation) { + Surface.ROTATION_180 -> 180f + Surface.ROTATION_270 -> 180f + else -> 0f +} + +private fun Display.isPortrait(): Boolean = + rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java deleted file mode 100644 index 4fc59d60ff62c..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static com.android.systemui.biometrics.SidefpsController.SFPS_AFFORDANCE_WIDTH; - -import android.annotation.NonNull; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.RectF; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; -import android.util.AttributeSet; -import android.view.Surface; -import android.widget.FrameLayout; - -/** - * A view containing a normal drawable view for sidefps events. - */ -public class SidefpsView extends FrameLayout { - private static final String TAG = "SidefpsView"; - private static final int POINTER_SIZE_PX = 50; - private static final int ROUND_RADIUS = 15; - - @NonNull private final RectF mSensorRect; - @NonNull private final Paint mSensorRectPaint; - @NonNull private final Paint mPointerText; - @NonNull private final Context mContext; - - // Used to obtain the sensor location. - @NonNull private FingerprintSensorPropertiesInternal mSensorProps; - @Surface.Rotation private int mOrientation; - - public SidefpsView(Context context, AttributeSet attrs) { - super(context, attrs); - super.setWillNotDraw(false); - mContext = context; - mPointerText = new Paint(0 /* flags */); - mPointerText.setAntiAlias(true); - mPointerText.setColor(Color.WHITE); - mPointerText.setTextSize(POINTER_SIZE_PX); - - mSensorRect = new RectF(); - mSensorRectPaint = new Paint(0 /* flags */); - mSensorRectPaint.setAntiAlias(true); - mSensorRectPaint.setColor(Color.BLUE); // TODO: Fix Color - mSensorRectPaint.setStyle(Paint.Style.FILL); - } - - void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) { - mSensorProps = properties; - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - canvas.drawRoundRect(mSensorRect, ROUND_RADIUS, ROUND_RADIUS, mSensorRectPaint); - int x, y; - if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) { - x = mSensorProps.sensorRadius + 10; - y = SFPS_AFFORDANCE_WIDTH / 2 + 15; - } else { - x = SFPS_AFFORDANCE_WIDTH / 2 - 10; - y = mSensorProps.sensorRadius + 30; - } - canvas.drawText( - ">", - x, - y, - mPointerText - ); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mOrientation = mContext.getDisplay().getRotation(); - if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) { - right = mSensorProps.sensorRadius * 2; - bottom = SFPS_AFFORDANCE_WIDTH; - } else { - right = SFPS_AFFORDANCE_WIDTH; - bottom = mSensorProps.sensorRadius * 2; - } - - mSensorRect.set( - 0, - 0, - right, - bottom); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 751cc8a51bed7..fccca24fa73a6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -129,7 +129,7 @@ public class UdfpsController implements DozeReceiver { @NonNull private final KeyguardBypassController mKeyguardBypassController; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final SystemClock mSystemClock; - @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener; + @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -558,14 +558,6 @@ public class UdfpsController implements DozeReceiver { mHbmProvider = hbmProvider.orElse(null); screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; - mOrientationListener = new BiometricOrientationEventListener( - context, - () -> { - onOrientationChanged(); - return Unit.INSTANCE; - }, - displayManager, - mainHandler); mKeyguardBypassController = keyguardBypassController; mConfigurationController = configurationController; mSystemClock = systemClock; @@ -573,6 +565,15 @@ public class UdfpsController implements DozeReceiver { mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists checkArgument(mSensorProps != null); + mOrientationListener = new BiometricDisplayListener( + context, + displayManager, + mainHandler, + new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps), + () -> { + onOrientationChanged(); + return Unit.INSTANCE; + }); mCoreLayoutParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt index 977b05ce150ce..01e60e28f67d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics +import android.graphics.Rect import android.hardware.biometrics.SensorProperties import android.hardware.display.DisplayManager import android.hardware.display.DisplayManagerGlobal @@ -30,8 +31,12 @@ import android.view.Display import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS import android.view.DisplayInfo import android.view.LayoutInflater +import android.view.View +import android.view.WindowInsets import android.view.WindowManager +import android.view.WindowMetrics import androidx.test.filters.SmallTest +import com.airbnb.lottie.LottieAnimationView import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor @@ -42,9 +47,13 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.eq +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.any +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @@ -66,11 +75,13 @@ class SidefpsControllerTest : SysuiTestCase() { @Mock lateinit var windowManager: WindowManager @Mock - lateinit var sidefpsView: SidefpsView + lateinit var sidefpsView: View @Mock lateinit var displayManager: DisplayManager @Mock lateinit var handler: Handler + @Captor + lateinit var overlayCaptor: ArgumentCaptor private val executor = FakeExecutor(FakeSystemClock()) private lateinit var overlayController: ISidefpsController @@ -79,6 +90,8 @@ class SidefpsControllerTest : SysuiTestCase() { @Before fun setup() { `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView) + `when`(sidefpsView.findViewById(eq(R.id.sidefps_animation))) + .thenReturn(mock(LottieAnimationView::class.java)) `when`(fingerprintManager.sensorPropertiesInternal).thenReturn( listOf( FingerprintSensorPropertiesInternal( @@ -99,6 +112,9 @@ class SidefpsControllerTest : SysuiTestCase() { DEFAULT_DISPLAY_ADJUSTMENTS ) ) + `when`(windowManager.maximumWindowMetrics).thenReturn( + WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED) + ) sideFpsController = SidefpsController( mContext, layoutInflater, fingerprintManager, windowManager, executor, @@ -121,4 +137,44 @@ class SidefpsControllerTest : SysuiTestCase() { executor.runAllReady() verify(displayManager).unregisterDisplayListener(any()) } + + @Test + fun testShowsAndHides() { + overlayController.show() + executor.runAllReady() + + verify(windowManager).addView(overlayCaptor.capture(), any()) + + reset(windowManager) + overlayController.hide() + executor.runAllReady() + + verify(windowManager, never()).addView(any(), any()) + verify(windowManager).removeView(eq(overlayCaptor.value)) + } + + @Test + fun testShowsOnce() { + repeat(5) { + overlayController.show() + executor.runAllReady() + } + + verify(windowManager).addView(any(), any()) + verify(windowManager, never()).removeView(any()) + } + + @Test + fun testHidesOnce() { + overlayController.show() + executor.runAllReady() + + repeat(5) { + overlayController.hide() + executor.runAllReady() + } + + verify(windowManager).addView(any(), any()) + verify(windowManager).removeView(any()) + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index db7f3d76b7aeb..8a11e8d1cc53e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -27,6 +27,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -67,6 +68,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient imp int sensorId, boolean isStrongBiometric, int statsClient, @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache, @Nullable IUdfpsOverlayController udfpsOverlayController, + @Nullable ISidefpsController sidefpsController, boolean allowBackgroundAuthentication, @NonNull FingerprintSensorPropertiesInternal sensorProps) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, @@ -76,7 +78,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient imp false /* isKeyguardBypassEnabled */); setRequestId(requestId); mLockoutCache = lockoutCache; - mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */); + mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); mSensorProps = sensorProps; mALSProbeCallback = createALSCallback(false /* startWithClient */); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index ca83dda3bc4ed..4c37934486e11 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -403,7 +403,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), - mUdfpsOverlayController, allowBackgroundAuthentication, + mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensors.get(sensorId).getSensorProperties()); scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index d2882aa4094c7..5f2f4cf6ef3c0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -629,7 +629,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mContext, mLazyDaemon, token, requestId, listener, userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, mSensorProperties.sensorId, isStrongBiometric, statsClient, - mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, + mTaskStackListener, mLockoutTracker, + mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensorProperties); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index afb4d16a88140..3058e2508f5fb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -67,6 +68,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient Date: Tue, 7 Sep 2021 08:53:25 -0700 Subject: [PATCH 2/2] Update side fsp sensor position via overlay. This is a temporary workaround and the overlay properties can be removed when the information is available from the HAL. Bug: 174868353 Fix: 188690214 Test: manual (enroll and verify icon location) Change-Id: I2b6826756814727368007e55bfc88d28c4a78bea --- .../biometrics/SensorLocationInternal.aidl | 19 +++ .../biometrics/SensorLocationInternal.java | 112 ++++++++++++++++++ .../FingerprintSensorPropertiesInternal.java | 83 +++++++------ core/res/res/values/config.xml | 14 +++ core/res/res/values/symbols.xml | 1 + .../keyguard/LockIconViewController.java | 6 +- .../biometrics/AuthRippleController.kt | 2 +- .../systemui/biometrics/SidefpsController.kt | 60 ++++++---- .../systemui/biometrics/UdfpsController.java | 27 +++-- .../biometrics/UdfpsDialogMeasureAdapter.java | 17 +-- .../systemui/biometrics/UdfpsEnrollView.java | 2 +- .../systemui/biometrics/UdfpsView.java | 6 +- ...uthBiometricFaceToFingerprintViewTest.java | 8 +- .../UdfpsDialogMeasureAdapterTest.java | 11 +- .../keyguard/LockIconViewControllerTest.java | 7 +- .../server/biometrics/AuthService.java | 6 +- .../fingerprint/aidl/FingerprintProvider.java | 54 ++++++++- .../hidl/Fingerprint21UdfpsMock.java | 3 +- .../aidl/FingerprintProviderTest.java | 13 +- 19 files changed, 346 insertions(+), 105 deletions(-) create mode 100644 core/java/android/hardware/biometrics/SensorLocationInternal.aidl create mode 100644 core/java/android/hardware/biometrics/SensorLocationInternal.java diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.aidl b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl new file mode 100644 index 0000000000000..098190449d53e --- /dev/null +++ b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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 android.hardware.biometrics; + +// @hide +parcelable SensorLocationInternal; diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.java b/core/java/android/hardware/biometrics/SensorLocationInternal.java new file mode 100644 index 0000000000000..fb25a2fcd8237 --- /dev/null +++ b/core/java/android/hardware/biometrics/SensorLocationInternal.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2021 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 android.hardware.biometrics; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The location of a sensor relative to a physical display. + * + * Note that the location may change depending on other attributes of the device, such as + * fold status, which are not yet included in this class. + * @hide + */ +public class SensorLocationInternal implements Parcelable { + + /** Default value to use when the sensor's location is unknown or undefined. */ + public static final SensorLocationInternal DEFAULT = new SensorLocationInternal("", 0, 0, 0); + + /** + * The stable display id. + */ + @NonNull + public final String displayId; + + /** + * The location of the center of the sensor if applicable. For example, sensors of type + * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the + * distance in pixels, measured from the left edge of the screen. + */ + public final int sensorLocationX; + + /** + * The location of the center of the sensor if applicable. For example, sensors of type + * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the + * distance in pixels, measured from the top edge of the screen. + * + */ + public final int sensorLocationY; + + /** + * The radius of the sensor if applicable. For example, sensors of type + * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius + * of the sensor, in pixels. + */ + public final int sensorRadius; + + public SensorLocationInternal(@Nullable String displayId, + int sensorLocationX, int sensorLocationY, int sensorRadius) { + this.displayId = displayId != null ? displayId : ""; + this.sensorLocationX = sensorLocationX; + this.sensorLocationY = sensorLocationY; + this.sensorRadius = sensorRadius; + } + + protected SensorLocationInternal(Parcel in) { + displayId = in.readString16NoHelper(); + sensorLocationX = in.readInt(); + sensorLocationY = in.readInt(); + sensorRadius = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(displayId); + dest.writeInt(sensorLocationX); + dest.writeInt(sensorLocationY); + dest.writeInt(sensorRadius); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = + new Creator() { + @Override + public SensorLocationInternal createFromParcel(Parcel in) { + return new SensorLocationInternal(in); + } + + @Override + public SensorLocationInternal[] newArray(int size) { + return new SensorLocationInternal[size]; + } + }; + + @Override + public String toString() { + return "[id: " + displayId + + ", x: " + sensorLocationX + + ", y: " + sensorLocationY + + ", r: " + sensorRadius + "]"; + } +} diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index 45c6b290be11c..4bf9a740f971e 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -21,7 +21,9 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFP import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC; import android.annotation.NonNull; +import android.annotation.Nullable; import android.hardware.biometrics.ComponentInfoInternal; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.Parcel; @@ -38,34 +40,14 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna */ public final @FingerprintSensorProperties.SensorType int sensorType; - /** - * The location of the center of the sensor if applicable. For example, sensors of type - * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the - * distance in pixels, measured from the left edge of the screen. - */ - public final int sensorLocationX; - - /** - * The location of the center of the sensor if applicable. For example, sensors of type - * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the - * distance in pixels, measured from the top edge of the screen. - * - */ - public final int sensorLocationY; - - /** - * The radius of the sensor if applicable. For example, sensors of type - * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius - * of the sensor, in pixels. - */ - public final int sensorRadius; + private final List mSensorLocations; public FingerprintSensorPropertiesInternal(int sensorId, @SensorProperties.Strength int strength, int maxEnrollmentsPerUser, @NonNull List componentInfo, @FingerprintSensorProperties.SensorType int sensorType, - boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY, - int sensorRadius) { + boolean resetLockoutRequiresHardwareAuthToken, + @NonNull List sensorLocations) { // IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not // required as it can only be generated/attested/verified by TEE components. // IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See @@ -73,9 +55,7 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna super(sensorId, strength, maxEnrollmentsPerUser, componentInfo, resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */); this.sensorType = sensorType; - this.sensorLocationX = sensorLocationX; - this.sensorLocationY = sensorLocationY; - this.sensorRadius = sensorRadius; + this.mSensorLocations = List.copyOf(sensorLocations); } /** @@ -88,16 +68,15 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna boolean resetLockoutRequiresHardwareAuthToken) { // TODO(b/179175438): Value should be provided from the HAL this(sensorId, strength, maxEnrollmentsPerUser, componentInfo, sensorType, - resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */, - 1636 /* sensorLocationY */, 130 /* sensorRadius */); + resetLockoutRequiresHardwareAuthToken, List.of(new SensorLocationInternal( + "" /* displayId */, 540 /* sensorLocationX */, 1636 /* sensorLocationY */, + 130 /* sensorRadius */))); } protected FingerprintSensorPropertiesInternal(Parcel in) { super(in); sensorType = in.readInt(); - sensorLocationX = in.readInt(); - sensorLocationY = in.readInt(); - sensorRadius = in.readInt(); + mSensorLocations = in.createTypedArrayList(SensorLocationInternal.CREATOR); } public static final Creator CREATOR = @@ -122,9 +101,7 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(sensorType); - dest.writeInt(sensorLocationX); - dest.writeInt(sensorLocationY); - dest.writeInt(sensorRadius); + dest.writeTypedList(mSensorLocations); } public boolean isAnyUdfpsType() { @@ -150,6 +127,44 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna } } + /** + * Get the default location. + * + * Use this method when the sensor's relationship to the displays on the device do not + * matter. + * @return + */ + @NonNull + public SensorLocationInternal getLocation() { + final SensorLocationInternal location = getLocation("" /* displayId */); + return location != null ? location : SensorLocationInternal.DEFAULT; + } + + /** + * Get the location of a sensor relative to a physical display layout. + * + * @param displayId stable display id + * @return location or null if none is specified + */ + @Nullable + public SensorLocationInternal getLocation(String displayId) { + for (SensorLocationInternal location : mSensorLocations) { + if (location.displayId.equals(displayId)) { + return location; + } + } + return null; + } + + /** + * Gets all locations relative to all supported display layouts. + * @return supported locations + */ + @NonNull + public List getAllLocations() { + return mSensorLocations; + } + @Override public String toString() { return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d5f4cad699c5f..c075648ae5e8e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4576,6 +4576,20 @@ --> + + + + + 50 diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7fe73e5067b9a..9c392ce335a9f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2608,6 +2608,7 @@ + diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 2a4022ca57b6b..a1151956b51aa 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -27,6 +27,7 @@ import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.media.AudioAttributes; import android.os.Process; @@ -330,8 +331,9 @@ public class LockIconViewController extends ViewController impleme private void updateLockIconLocation() { if (mUdfpsSupported) { FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0); - mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY), - props.sensorRadius); + final SensorLocationInternal location = props.getLocation(); + mView.setCenterLocation(new PointF(location.sensorLocationX, location.sensorLocationY), + location.sensorRadius); } else { mView.setCenterLocation( new PointF(mWidthPixels / 2, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 9c818fff50187..b4b296ed9a6cc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -223,7 +223,7 @@ class AuthRippleController @Inject constructor( private fun updateUdfpsDependentParams() { authController.udfpsProps?.let { if (it.size > 0) { - udfpsRadius = it[0].sensorRadius.toFloat() + udfpsRadius = it[0].location.sensorRadius.toFloat() udfpsController = udfpsControllerProvider.get() if (mView.isAttachedToWindow) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt index 54d5934d880d1..c0731b25f7b2e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics import android.content.Context import android.graphics.PixelFormat +import android.graphics.Rect import android.hardware.display.DisplayManager import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintSensorPropertiesInternal @@ -56,21 +57,8 @@ class SidefpsController @Inject constructor( @VisibleForTesting val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager ?.sensorPropertiesInternal - ?.filter { it.isAnySidefpsType } - // TODO(b/188690214): remove - should directly come from HAL - ?.map { props -> - FingerprintSensorPropertiesInternal( - props.sensorId, - props.sensorStrength, - props.maxEnrollmentsPerUser, - props.componentInfo, - props.sensorType, - props.resetLockoutRequiresHardwareAuthToken, - 25 /* sensorLocationX */, - 610 /* sensorLocationY */, - 112 /* sensorRadius */ - ) - }?.firstOrNull() ?: throw IllegalStateException("no side fingerprint sensor") + ?.firstOrNull { it.isAnySidefpsType } + ?: throw IllegalStateException("no side fingerprint sensor") @VisibleForTesting val orientationListener = BiometricDisplayListener( @@ -131,29 +119,49 @@ class SidefpsController @Inject constructor( private fun createOverlayForDisplay(): View { val view = layoutInflater.inflate(R.layout.sidefps_view, null, false) - val display = context.display!! - val isPortrait = display.isPortrait() - val size = windowManager.maximumWindowMetrics.bounds - val displayWidth = if (isPortrait) size.width() else size.height() - val displayHeight = if (isPortrait) size.height() else size.width() val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView lottie.setAnimation(display.asSideFpsAnimation()) view.rotation = display.asSideFpsAnimationRotation() + updateOverlayParams(display, lottie.composition?.bounds ?: Rect()) + lottie.addLottieOnCompositionLoadedListener { + if (overlayView == view) { + updateOverlayParams(display, it.bounds) + windowManager.updateViewLayout(overlayView, overlayViewParams) + } + } + + return view + } + + private fun updateOverlayParams(display: Display, bounds: Rect) { + val isPortrait = display.isPortrait() + val size = windowManager.maximumWindowMetrics.bounds + val displayWidth = if (isPortrait) size.width() else size.height() + val displayHeight = if (isPortrait) size.height() else size.width() + val offsets = sensorProps.getLocation(display.uniqueId).let { location -> + if (location == null) { + Log.w(TAG, "No location specified for display: ${display.uniqueId}") + } + location ?: sensorProps.location + } + // ignore sensorLocationX and sensorRadius since it's assumed to be on the side // of the device and centered at sensorLocationY val (x, y) = when (display.rotation) { - Surface.ROTATION_90 -> Pair(sensorProps.sensorLocationY, 0) - Surface.ROTATION_270 -> Pair(displayHeight - sensorProps.sensorLocationY, displayWidth) - Surface.ROTATION_180 -> Pair(0, displayHeight - sensorProps.sensorLocationY) - else -> Pair(displayWidth, sensorProps.sensorLocationY) + Surface.ROTATION_90 -> + Pair(offsets.sensorLocationY, 0) + Surface.ROTATION_270 -> + Pair(displayHeight - offsets.sensorLocationY - bounds.width(), displayWidth) + Surface.ROTATION_180 -> + Pair(0, displayHeight - offsets.sensorLocationY - bounds.height()) + else -> + Pair(displayWidth, offsets.sensorLocationY) } overlayViewParams.x = x overlayViewParams.y = y - - return view } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index fccca24fa73a6..63fb8e2b8ac1e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -33,6 +33,7 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RectF; import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -643,10 +644,11 @@ public class UdfpsController implements DozeReceiver { // on lockscreen and for the udfps light reveal animation on keyguard. // Keyguard is only shown in portrait mode for now, so this will need to // be updated if that ever changes. - return new RectF(mSensorProps.sensorLocationX - mSensorProps.sensorRadius, - mSensorProps.sensorLocationY - mSensorProps.sensorRadius, - mSensorProps.sensorLocationX + mSensorProps.sensorRadius, - mSensorProps.sensorLocationY + mSensorProps.sensorRadius); + final SensorLocationInternal location = mSensorProps.getLocation(); + return new RectF(location.sensorLocationX - location.sensorRadius, + location.sensorLocationY - location.sensorRadius, + location.sensorLocationX + location.sensorRadius, + location.sensorLocationY + location.sensorRadius); } private void updateOverlay() { @@ -670,10 +672,11 @@ public class UdfpsController implements DozeReceiver { } // Default dimensions assume portrait mode. - mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius - paddingX; - mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingY; - mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius + 2 * paddingX; - mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius + 2 * paddingY; + final SensorLocationInternal location = mSensorProps.getLocation(); + mCoreLayoutParams.x = location.sensorLocationX - location.sensorRadius - paddingX; + mCoreLayoutParams.y = location.sensorLocationY - location.sensorRadius - paddingY; + mCoreLayoutParams.height = 2 * location.sensorRadius + 2 * paddingX; + mCoreLayoutParams.width = 2 * location.sensorRadius + 2 * paddingY; Point p = new Point(); // Gets the size based on the current rotation of the display. @@ -686,9 +689,9 @@ public class UdfpsController implements DozeReceiver { && mKeyguardUpdateMonitor.isGoingToSleep()) { break; } - mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius + mCoreLayoutParams.x = location.sensorLocationY - location.sensorRadius - paddingX; - mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius + mCoreLayoutParams.y = p.y - location.sensorLocationX - location.sensorRadius - paddingY; break; @@ -697,9 +700,9 @@ public class UdfpsController implements DozeReceiver { && mKeyguardUpdateMonitor.isGoingToSleep()) { break; } - mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius + mCoreLayoutParams.x = p.x - location.sensorLocationY - location.sensorRadius - paddingX; - mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius + mCoreLayoutParams.y = location.sensorLocationX - location.sensorRadius - paddingY; break; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java index 7ccfb865cd5a2..6cc8acf07c50d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.util.Log; import android.view.Surface; @@ -93,7 +94,7 @@ public class UdfpsDialogMeasureAdapter { // Go through each of the children and do the custom measurement. int totalHeight = 0; final int numChildren = mView.getChildCount(); - final int sensorDiameter = mSensorProps.sensorRadius * 2; + final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2; for (int i = 0; i < numChildren; i++) { final View child = mView.getChildAt(i); if (child.getId() == R.id.biometric_icon_frame) { @@ -160,7 +161,7 @@ public class UdfpsDialogMeasureAdapter { final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape( mSensorProps, displayWidth, dialogMargin, horizontalInset); - final int sensorDiameter = mSensorProps.sensorRadius * 2; + final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2; final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth; int remeasuredHeight = 0; @@ -257,10 +258,10 @@ public class UdfpsDialogMeasureAdapter { @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx, int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx, int navbarBottomInsetPx) { - + final SensorLocationInternal location = sensorProperties.getLocation(); final int sensorDistanceFromBottom = displayHeightPx - - sensorProperties.sensorLocationY - - sensorProperties.sensorRadius; + - location.sensorLocationY + - location.sensorRadius; final int spacerHeight = sensorDistanceFromBottom - textIndicatorHeightPx @@ -318,10 +319,10 @@ public class UdfpsDialogMeasureAdapter { static int calculateHorizontalSpacerWidthForLandscape( @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx, int dialogMarginPx, int navbarHorizontalInsetPx) { - + final SensorLocationInternal location = sensorProperties.getLocation(); final int sensorDistanceFromEdge = displayWidthPx - - sensorProperties.sensorLocationY - - sensorProperties.sensorRadius; + - location.sensorLocationY + - location.sensorRadius; final int horizontalPadding = sensorDistanceFromEdge - dialogMarginPx diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index d9edef48b3d4c..c83006d8092a1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -63,7 +63,7 @@ public class UdfpsEnrollView extends UdfpsAnimationView { void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) { View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view); - final int sensorHeight = sensorProps.sensorRadius * 2; + final int sensorHeight = sensorProps.getLocation().sensorRadius * 2; final int sensorWidth = sensorHeight; ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams(); params.width = sensorWidth; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 15f77ffc08fd4..6d31ef0e77015 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -25,6 +25,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.RectF; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Build; import android.os.UserHandle; @@ -141,11 +142,12 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin : mAnimationViewController.getPaddingX(); int paddingY = mAnimationViewController == null ? 0 : mAnimationViewController.getPaddingY(); + final SensorLocationInternal location = mSensorProps.getLocation(); mSensorRect.set( paddingX, paddingY, - 2 * mSensorProps.sensorRadius + paddingX, - 2 * mSensorProps.sensorRadius + paddingY); + 2 * location.sensorRadius + paddingX, + 2 * location.sensorRadius + paddingY); if (mAnimationViewController != null) { mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java index b6d1e42a47635..619d48d1e3068 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.hardware.biometrics.ComponentInfoInternal; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -257,9 +258,10 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase { componentInfo, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, true /* resetLockoutRequiresHardwareAuthToken */, - 540 /* sensorLocationX */, - 1600 /* sensorLocationY */, - 100 /* sensorRadius */); + List.of(new SensorLocationInternal("" /* displayId */, + 540 /* sensorLocationX */, + 1600 /* sensorLocationY */, + 100 /* sensorRadius */))); } public class TestableView extends AuthBiometricFaceToFingerprintView { diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java index 88b4039fd2cd8..27755edecba64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java @@ -19,6 +19,7 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertEquals; import android.hardware.biometrics.ComponentInfoInternal; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -61,8 +62,9 @@ public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase { 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, componentInfo, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY, - sensorRadius); + true /* resetLockoutRequiresHardwareAuthToken */, + List.of(new SensorLocationInternal("" /* displayId */, + sensorLocationX, sensorLocationY, sensorRadius))); assertEquals(970, UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForPortrait( @@ -125,8 +127,9 @@ public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase { 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, componentInfo, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY, - sensorRadius); + true /* resetLockoutRequiresHardwareAuthToken */, + List.of(new SensorLocationInternal("" /* displayId */, + sensorLocationX, sensorLocationY, sensorRadius))); assertEquals(1205, UdfpsDialogMeasureAdapter.calculateHorizontalSpacerWidthForLandscape( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index d279bbb02cee5..3e9fbcfa1e9d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Vibrator; import android.testing.AndroidTestingRunner; @@ -132,7 +133,8 @@ public class LockIconViewControllerTest extends SysuiTestCase { /* component info */ new ArrayList<>(), /* sensorType */ 3, /* resetLockoutRequiresHwToken */ false, - (int) udfpsLocation.x, (int) udfpsLocation.y, radius); + List.of(new SensorLocationInternal("" /* displayId */, + (int) udfpsLocation.x, (int) udfpsLocation.y, radius))); when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); @@ -165,7 +167,8 @@ public class LockIconViewControllerTest extends SysuiTestCase { /* component info */ new ArrayList<>(), /* sensorType */ 3, /* resetLockoutRequiresHwToken */ false, - (int) udfpsLocation.x, (int) udfpsLocation.y, radius); + List.of(new SensorLocationInternal("" /* displayId */, + (int) udfpsLocation.x, (int) udfpsLocation.y, radius))); when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index b42f8980d1c0c..9c8ccd946b7f8 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -47,6 +47,7 @@ import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.biometrics.SensorPropertiesInternal; import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; @@ -766,8 +767,9 @@ public class AuthService extends SystemService { if (isUdfps && udfpsProps.length == 3) { return new FingerprintSensorPropertiesInternal(sensorId, Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser, - componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken, udfpsProps[0], - udfpsProps[1], udfpsProps[2]); + componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken, + List.of(new SensorLocationInternal("" /* display */, + udfpsProps[0], udfpsProps[1], udfpsProps[2]))); } else { return new FingerprintSensorPropertiesInternal(sensorId, Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 4c37934486e11..0defc3fb6a50f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -25,10 +25,12 @@ import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.Context; import android.content.pm.UserInfo; +import android.content.res.TypedArray; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.biometrics.SensorLocationInternal; import android.hardware.biometrics.common.ComponentInfo; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; @@ -145,6 +147,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); + final List workaroundLocations = getWorkaroundSensorProps(context); + for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; @@ -164,9 +168,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi componentInfo, prop.sensorType, true /* resetLockoutRequiresHardwareAuthToken */, - prop.sensorLocations[0].sensorLocationX, - prop.sensorLocations[0].sensorLocationY, - prop.sensorLocations[0].sensorRadius); + !workaroundLocations.isEmpty() ? workaroundLocations : + List.of(new SensorLocationInternal( + "" /* displayId */, + prop.sensorLocations[0].sensorLocationX, + prop.sensorLocations[0].sensorLocationY, + prop.sensorLocations[0].sensorRadius))); final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher); @@ -647,4 +654,45 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi void setTestHalEnabled(boolean enabled) { mTestHalEnabled = enabled; } + + // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL) + // reads values via an overlay instead of querying the HAL + @NonNull + private List getWorkaroundSensorProps(@NonNull Context context) { + final List sensorLocations = new ArrayList<>(); + + final TypedArray sfpsProps = context.getResources().obtainTypedArray( + com.android.internal.R.array.config_sfps_sensor_props); + for (int i = 0; i < sfpsProps.length(); i++) { + final int id = sfpsProps.getResourceId(i, -1); + if (id > 0) { + final SensorLocationInternal location = parseSensorLocation( + context.getResources().obtainTypedArray(id)); + if (location != null) { + sensorLocations.add(location); + } + } + } + sfpsProps.recycle(); + + return sensorLocations; + } + + @Nullable + private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) { + if (array == null) { + return null; + } + + try { + return new SensorLocationInternal( + array.getString(0), + array.getInt(1, 0), + array.getInt(2, 0), + array.getInt(3, 0)); + } catch (Exception e) { + Slog.w(getTag(), "malformed sensor location", e); + } + return null; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 79ad8e1a5c705..dd68b4d37e2a4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -426,8 +426,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage mSensorProperties = new FingerprintSensorPropertiesInternal(sensorProps.sensorId, sensorProps.sensorStrength, maxTemplatesAllowed, sensorProps.componentInfo, FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - resetLockoutRequiresHardwareAuthToken, sensorProps.sensorLocationX, - sensorProps.sensorLocationY, sensorProps.sensorRadius); + resetLockoutRequiresHardwareAuthToken, sensorProps.getAllLocations()); mMockHalResultController = controller; mUserHasTrust = new SparseBooleanArray(); mTrustManager = context.getSystemService(TrustManager.class); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index b51918e24b13e..8b7c90d985b57 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -19,10 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.hardware.biometrics.common.CommonProps; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorLocation; @@ -56,6 +59,8 @@ public class FingerprintProviderTest { @Mock private Context mContext; @Mock + private Resources mResources; + @Mock private UserManager mUserManager; @Mock private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @@ -74,19 +79,21 @@ public class FingerprintProviderTest { public void setUp() { MockitoAnnotations.initMocks(this); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.obtainTypedArray(anyInt())).thenReturn(mock(TypedArray.class)); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>()); final SensorProps sensor1 = new SensorProps(); sensor1.commonProps = new CommonProps(); sensor1.commonProps.sensorId = 0; - sensor1.sensorLocations = new SensorLocation[] {new SensorLocation()}; + sensor1.sensorLocations = new SensorLocation[]{new SensorLocation()}; final SensorProps sensor2 = new SensorProps(); sensor2.commonProps = new CommonProps(); sensor2.commonProps.sensorId = 1; - sensor2.sensorLocations = new SensorLocation[] {new SensorLocation()}; + sensor2.sensorLocations = new SensorLocation[]{new SensorLocation()}; - mSensorProps = new SensorProps[] {sensor1, sensor2}; + mSensorProps = new SensorProps[]{sensor1, sensor2}; mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);