/* * 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); } private int mEnrollmentSteps = -1; private int mEnrollmentRemaining = 0; private Listener mListener; private boolean mEnrolling; private Handler mHandler = new Handler(); private boolean mDone; private ArrayList 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 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; } public void setListener(Listener listener) { mListener = listener; if (mListener != null) { for (int i=0; i