When strong auth (PIN, password, or pattern) is removed from a user, fingerprints enrolled for that user should also be removed. Then, if the user has a work profile guarded by a unified work challenge, the work profile's fingerprints should also be removed. Previously, when removing the fingerprints of the current user, ChooseLockGeneric checked the finger id of the onRemovalSucceeded() and onRemovalError() callbacks, and assumed the removal had completed when the id was 0. Only after this would it initiate the removal of work profile fingerprints, if any. However, the finger id is actually non-zero even for the user's last fingerprint. This means the work profile fingerprints (under unified challenge) were never removed. Another more visible symptom was that when the user removed the device lock by choosing "None" or "Swipe" in ChooseLockGeneric, the activity failed to exit, since finish() is called only at the end of the removal flow which was not executed. In this CL, we check the number of remaining fingerprints instead of relying on the finger id, thus allowing the removal flow to complete and the activity to finish(). Bug: 37938345 Test: manual, both with and without work profile Test: make SettingsRoboTests Change-Id: Ic04fd01177ad6d4a061023a4b6889af585f8f2b7
150 lines
4.8 KiB
Java
150 lines
4.8 KiB
Java
/*
|
|
* Copyright (C) 2017 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.fingerprint;
|
|
|
|
import android.annotation.Nullable;
|
|
import android.content.Context;
|
|
import android.hardware.fingerprint.Fingerprint;
|
|
import android.hardware.fingerprint.FingerprintManager;
|
|
import android.os.Bundle;
|
|
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
import android.os.UserHandle;
|
|
import java.util.Queue;
|
|
import java.util.LinkedList;
|
|
import android.util.Log;
|
|
|
|
/**
|
|
* Sidecar fragment to handle the state around fingerprint removal.
|
|
*/
|
|
public class FingerprintRemoveSidecar extends InstrumentedPreferenceFragment {
|
|
|
|
private static final String TAG = "FingerprintRemoveSidecar";
|
|
private Listener mListener;
|
|
private Fingerprint mFingerprintRemoving;
|
|
private Queue<Object> mFingerprintsRemoved;
|
|
FingerprintManager mFingerprintManager;
|
|
|
|
private class RemovalError {
|
|
Fingerprint fingerprint;
|
|
int errMsgId;
|
|
CharSequence errString;
|
|
public RemovalError(Fingerprint fingerprint, int errMsgId, CharSequence errString) {
|
|
this.fingerprint = fingerprint;
|
|
this.errMsgId = errMsgId;
|
|
this.errString = errString;
|
|
}
|
|
}
|
|
|
|
private FingerprintManager.RemovalCallback
|
|
mRemoveCallback = new FingerprintManager.RemovalCallback() {
|
|
@Override
|
|
public void onRemovalSucceeded(Fingerprint fingerprint, int remaining) {
|
|
if (mListener != null) {
|
|
mListener.onRemovalSucceeded(fingerprint);
|
|
} else {
|
|
mFingerprintsRemoved.add(fingerprint);
|
|
};
|
|
mFingerprintRemoving = null;
|
|
}
|
|
|
|
@Override
|
|
public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {
|
|
if (mListener != null) {
|
|
mListener.onRemovalError(fp, errMsgId, errString);
|
|
} else {
|
|
mFingerprintsRemoved.add(new RemovalError(fp, errMsgId, errString));
|
|
}
|
|
mFingerprintRemoving = null;
|
|
}
|
|
};
|
|
|
|
public void startRemove(Fingerprint fingerprint, int userId) {
|
|
if (mFingerprintRemoving != null) {
|
|
Log.e(TAG, "Remove already in progress");
|
|
return;
|
|
}
|
|
if (userId != UserHandle.USER_NULL) {
|
|
mFingerprintManager.setActiveUser(userId);
|
|
}
|
|
mFingerprintRemoving = fingerprint;
|
|
mFingerprintManager.remove(fingerprint, userId, mRemoveCallback);;
|
|
}
|
|
|
|
public FingerprintRemoveSidecar() {
|
|
mFingerprintsRemoved = new LinkedList<>();
|
|
}
|
|
|
|
public void setFingerprintManager(FingerprintManager fingerprintManager) {
|
|
mFingerprintManager = fingerprintManager;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setRetainInstance(true);
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Context context) {
|
|
super.onAttach(context);
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
}
|
|
|
|
@Override
|
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
|
}
|
|
|
|
public void setListener(Listener listener) {
|
|
if (mListener == null && listener != null) {
|
|
while (!mFingerprintsRemoved.isEmpty()) {
|
|
Object o = mFingerprintsRemoved.poll();
|
|
if (o instanceof Fingerprint) {
|
|
listener.onRemovalSucceeded((Fingerprint)o);
|
|
} else if (o instanceof RemovalError) {
|
|
RemovalError e = (RemovalError) o;
|
|
listener.onRemovalError(e.fingerprint, e.errMsgId, e.errString);
|
|
}
|
|
}
|
|
}
|
|
mListener = listener;
|
|
}
|
|
|
|
public interface Listener {
|
|
void onRemovalSucceeded(Fingerprint fingerprint);
|
|
void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString);
|
|
}
|
|
|
|
final boolean isRemovingFingerprint(int fid) {
|
|
return inProgress() && mFingerprintRemoving.getFingerId() == fid;
|
|
}
|
|
|
|
final boolean inProgress() {
|
|
return mFingerprintRemoving != null;
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.FINGERPRINT_REMOVE_SIDECAR;
|
|
}
|
|
|
|
}
|