blob: 97d46a420e3bf80069cb5e085373d507f0ceb6c5 [file] [log] [blame]
/*
* Copyright (C) 2018 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.settings.biometrics;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.UserHandle;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
import java.util.ArrayList;
/**
* Abstract sidecar fragment to handle the state around biometric enrollment. This sidecar manages
* the state of enrollment throughout the activity lifecycle so the app can continue after an
* event like rotation.
*/
public abstract class BiometricEnrollSidecar extends InstrumentedFragment {
public interface Listener {
void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
void onEnrollmentError(int errMsgId, CharSequence errString);
void onEnrollmentProgressChange(int steps, int remaining);
/**
* Called when a fingerprint image has been acquired.
* @param isAcquiredGood whether the fingerprint image was good.
*/
default void onAcquired(boolean isAcquiredGood) { }
/**
* Called when a pointer down event has occurred.
*/
default void onPointerDown(int sensorId) { }
/**
* Called when a pointer up event has occurred.
*/
default void onPointerUp(int sensorId) { }
}
private int mEnrollmentSteps = -1;
private int mEnrollmentRemaining = 0;
private Listener mListener;
private boolean mEnrolling;
private Handler mHandler = new Handler();
private boolean mDone;
private ArrayList<QueuedEvent> mQueuedEvents;
protected CancellationSignal mEnrollmentCancel;
protected byte[] mToken;
protected int mUserId;
private abstract class QueuedEvent {
public abstract void send(Listener listener);
}
private class QueuedEnrollmentProgress extends QueuedEvent {
int enrollmentSteps;
int remaining;
public QueuedEnrollmentProgress(int enrollmentSteps, int remaining) {
this.enrollmentSteps = enrollmentSteps;
this.remaining = remaining;
}
@Override
public void send(Listener listener) {
listener.onEnrollmentProgressChange(enrollmentSteps, remaining);
}
}
private class QueuedEnrollmentHelp extends QueuedEvent {
int helpMsgId;
CharSequence helpString;
public QueuedEnrollmentHelp(int helpMsgId, CharSequence helpString) {
this.helpMsgId = helpMsgId;
this.helpString = helpString;
}
@Override
public void send(Listener listener) {
listener.onEnrollmentHelp(helpMsgId, helpString);
}
}
private class QueuedEnrollmentError extends QueuedEvent {
int errMsgId;
CharSequence errString;
public QueuedEnrollmentError(int errMsgId, CharSequence errString) {
this.errMsgId = errMsgId;
this.errString = errString;
}
@Override
public void send(Listener listener) {
listener.onEnrollmentError(errMsgId, errString);
}
}
private class QueuedAcquired extends QueuedEvent {
private final boolean isAcquiredGood;
public QueuedAcquired(boolean isAcquiredGood) {
this.isAcquiredGood = isAcquiredGood;
}
@Override
public void send(Listener listener) {
listener.onAcquired(isAcquiredGood);
}
}
private class QueuedPointerDown extends QueuedEvent {
private final int sensorId;
public QueuedPointerDown(int sensorId) {
this.sensorId = sensorId;
}
@Override
public void send(Listener listener) {
listener.onPointerDown(sensorId);
}
}
private class QueuedPointerUp extends QueuedEvent {
private final int sensorId;
public QueuedPointerUp(int sensorId) {
this.sensorId = sensorId;
}
@Override
public void send(Listener listener) {
listener.onPointerUp(sensorId);
}
}
private final Runnable mTimeoutRunnable = new Runnable() {
@Override
public void run() {
cancelEnrollment();
}
};
public BiometricEnrollSidecar() {
mQueuedEvents = new ArrayList<>();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mToken = activity.getIntent().getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
mUserId = activity.getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
}
@Override
public void onStart() {
super.onStart();
if (!mEnrolling) {
startEnrollment();
}
}
@Override
public void onStop() {
super.onStop();
if (!getActivity().isChangingConfigurations()) {
cancelEnrollment();
}
}
protected void startEnrollment() {
mHandler.removeCallbacks(mTimeoutRunnable);
mEnrollmentSteps = -1;
mEnrollmentCancel = new CancellationSignal();
mEnrolling = true;
}
public boolean cancelEnrollment() {
mHandler.removeCallbacks(mTimeoutRunnable);
if (mEnrolling) {
mEnrollmentCancel.cancel();
mEnrolling = false;
mEnrollmentSteps = -1;
return true;
}
return false;
}
protected void onEnrollmentProgress(int remaining) {
if (mEnrollmentSteps == -1) {
mEnrollmentSteps = remaining;
}
mEnrollmentRemaining = remaining;
mDone = remaining == 0;
if (mListener != null) {
mListener.onEnrollmentProgressChange(mEnrollmentSteps, remaining);
} else {
mQueuedEvents.add(new QueuedEnrollmentProgress(mEnrollmentSteps, remaining));
}
}
protected void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
if (mListener != null) {
mListener.onEnrollmentHelp(helpMsgId, helpString);
} else {
mQueuedEvents.add(new QueuedEnrollmentHelp(helpMsgId, helpString));
}
}
protected void onEnrollmentError(int errMsgId, CharSequence errString) {
if (mListener != null) {
mListener.onEnrollmentError(errMsgId, errString);
} else {
mQueuedEvents.add(new QueuedEnrollmentError(errMsgId, errString));
}
mEnrolling = false;
}
protected void onAcquired(boolean isAcquiredGood) {
if (mListener != null) {
mListener.onAcquired(isAcquiredGood);
} else {
mQueuedEvents.add(new QueuedAcquired(isAcquiredGood));
}
}
protected void onPointerDown(int sensorId) {
if (mListener != null) {
mListener.onPointerDown(sensorId);
} else {
mQueuedEvents.add(new QueuedPointerDown(sensorId));
}
}
protected void onPointerUp(int sensorId) {
if (mListener != null) {
mListener.onPointerUp(sensorId);
} else {
mQueuedEvents.add(new QueuedPointerUp(sensorId));
}
}
public void setListener(Listener listener) {
mListener = listener;
if (mListener != null) {
for (int i=0; i<mQueuedEvents.size(); i++) {
QueuedEvent event = mQueuedEvents.get(i);
event.send(mListener);
}
mQueuedEvents.clear();
}
}
public int getEnrollmentSteps() {
return mEnrollmentSteps;
}
public int getEnrollmentRemaining() {
return mEnrollmentRemaining;
}
public boolean isDone() {
return mDone;
}
public boolean isEnrolling() {
return mEnrolling;
}
}