Persist fingerprint names
Bug: 20469328
Change-Id: I8f4a988687bfb78c36cc7cf187103a9d93ed4535
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 0faccc6..aa143e4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -31,7 +31,6 @@
import com.android.server.SystemService;
-import android.hardware.fingerprint.FingerprintUtils;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
@@ -93,6 +92,7 @@
private Context mContext;
private int mHalDeviceId;
private int mFailedAttempts;
+ private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
private final Runnable mLockoutReset = new Runnable() {
@Override
public void run() {
@@ -172,7 +172,6 @@
* @return true if the operation is done, i.e. authentication completed
*/
boolean dispatchNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
- ContentResolver contentResolver = mContext.getContentResolver();
boolean operationCompleted = false;
int fpId;
int groupId;
@@ -198,7 +197,7 @@
remaining = arg3;
operationCompleted = clientMonitor.sendEnrollResult(fpId, groupId, remaining);
if (remaining == 0) {
- addTemplateForUser(clientMonitor, contentResolver, fpId);
+ addTemplateForUser(clientMonitor, fpId);
operationCompleted = true; // enroll completed
}
break;
@@ -207,7 +206,7 @@
groupId = arg2;
operationCompleted = clientMonitor.sendRemoved(fpId, groupId);
if (fpId != 0) {
- removeTemplateForUser(clientMonitor, contentResolver, fpId);
+ removeTemplateForUser(clientMonitor, fpId);
}
break;
}
@@ -252,16 +251,12 @@
return false;
}
- private void removeTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver,
- final int fingerId) {
- FingerprintUtils.removeFingerprintIdForUser(fingerId, contentResolver,
- clientMonitor.userId);
+ private void removeTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
+ mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, clientMonitor.userId);
}
- private void addTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver,
- final int fingerId) {
- FingerprintUtils.addFingerprintIdForUser(contentResolver, fingerId,
- clientMonitor.userId);
+ private void addTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
+ mFingerprintUtils.addFingerprintForUser(mContext, fingerId, clientMonitor.userId);
}
void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
@@ -345,24 +340,11 @@
}
public List<Fingerprint> getEnrolledFingerprints(int groupId) {
- ContentResolver resolver = mContext.getContentResolver();
- int[] ids = FingerprintUtils.getFingerprintIdsForUser(resolver, groupId);
- List<Fingerprint> result = new ArrayList<Fingerprint>();
- for (int i = 0; i < ids.length; i++) {
- // TODO: persist names in Settings
- CharSequence name = "Finger" + ids[i];
- final int group = 0; // TODO
- final int fingerId = ids[i];
- final long deviceId = 0; // TODO
- Fingerprint item = new Fingerprint(name, 0, ids[i], 0);
- result.add(item);
- }
- return result;
+ return mFingerprintUtils.getFingerprintsForUser(mContext, groupId);
}
public boolean hasEnrolledFingerprints(int groupId) {
- ContentResolver resolver = mContext.getContentResolver();
- return FingerprintUtils.getFingerprintIdsForUser(resolver, groupId).length > 0;
+ return mFingerprintUtils.getFingerprintsForUser(mContext, groupId).size() > 0;
}
void checkPermission(String permission) {
@@ -596,7 +578,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- Slog.w(TAG, "rename id=" + fingerId + ",gid=" + groupId + ",name=" + name);
+ mFingerprintUtils.renameFingerprintForUser(mContext, fingerId, groupId, name);
}
});
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
new file mode 100644
index 0000000..1e6e105
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.Vibrator;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.List;
+
+/**
+ * Utility class for dealing with fingerprints and fingerprint settings.
+ */
+public class FingerprintUtils {
+
+ private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
+ private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
+
+ private static final Object sInstanceLock = new Object();
+ private static FingerprintUtils sInstance;
+
+ @GuardedBy("this")
+ private final SparseArray<FingerprintsUserState> mUsers = new SparseArray<>();
+
+ public static FingerprintUtils getInstance() {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new FingerprintUtils();
+ }
+ }
+ return sInstance;
+ }
+
+ private FingerprintUtils() {
+ }
+
+ public List<Fingerprint> getFingerprintsForUser(Context ctx, int userId) {
+ return getStateForUser(ctx, userId).getFingerprints();
+ }
+
+ public void addFingerprintForUser(Context ctx, int fingerId, int userId) {
+ getStateForUser(ctx, userId).addFingerprint(fingerId);
+ }
+
+ public void removeFingerprintIdForUser(Context ctx, int fingerId, int userId) {
+ getStateForUser(ctx, userId).removeFingerprint(fingerId);
+ }
+
+ public void renameFingerprintForUser(Context ctx, int fingerId, int userId, CharSequence name) {
+ getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
+ }
+
+ public static void vibrateFingerprintError(Context context) {
+ Vibrator vibrator = context.getSystemService(Vibrator.class);
+ if (vibrator != null) {
+ vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
+ }
+ }
+
+ public static void vibrateFingerprintSuccess(Context context) {
+ Vibrator vibrator = context.getSystemService(Vibrator.class);
+ if (vibrator != null) {
+ vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
+ }
+ }
+
+ private FingerprintsUserState getStateForUser(Context ctx, int userId) {
+ synchronized (this) {
+ FingerprintsUserState state = mUsers.get(userId);
+ if (state == null) {
+ state = new FingerprintsUserState(ctx, userId);
+ mUsers.put(userId, state);
+ }
+ return state;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
new file mode 100644
index 0000000..33177b4
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class managing the set of fingerprint per user across device reboots.
+ */
+class FingerprintsUserState {
+
+ private static final String TAG = "FingerprintState";
+ private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
+
+ private static final String TAG_FINGERPRINTS = "fingerprints";
+ private static final String TAG_FINGERPRINT = "fingerprint";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_GROUP_ID = "groupId";
+ private static final String ATTR_FINGER_ID = "fingerId";
+ private static final String ATTR_DEVICE_ID = "deviceId";
+
+ private final File mFile;
+
+ @GuardedBy("this")
+ private final ArrayList<Fingerprint> mFingerprints = new ArrayList<>();
+ private final Context mCtx;
+
+ public FingerprintsUserState(Context ctx, int userId) {
+ mFile = getFileForUser(userId);
+ mCtx = ctx;
+ synchronized (this) {
+ readStateSyncLocked();
+ }
+ }
+
+ public void addFingerprint(int fingerId) {
+ synchronized (this) {
+ mFingerprints.add(new Fingerprint(getDefaultFingerprintName(fingerId), 0, fingerId, 0));
+ scheduleWriteStateLocked();
+ }
+ }
+
+ public void removeFingerprint(int fingerId) {
+ synchronized (this) {
+ for (int i = 0; i < mFingerprints.size(); i++) {
+ if (mFingerprints.get(i).getFingerId() == fingerId) {
+ mFingerprints.remove(i);
+ scheduleWriteStateLocked();
+ break;
+ }
+ }
+ }
+ }
+
+ public void renameFingerprint(int fingerId, CharSequence name) {
+ synchronized (this) {
+ for (int i = 0; i < mFingerprints.size(); i++) {
+ if (mFingerprints.get(i).getFingerId() == fingerId) {
+ Fingerprint old = mFingerprints.get(i);
+ mFingerprints.set(i, new Fingerprint(name, old.getGroupId(), old.getFingerId(),
+ old.getDeviceId()));
+ scheduleWriteStateLocked();
+ break;
+ }
+ }
+ }
+ }
+
+ public List<Fingerprint> getFingerprints() {
+ synchronized (this) {
+ return getCopy(mFingerprints);
+ }
+ }
+
+ private String getDefaultFingerprintName(int fingerId) {
+ return mCtx.getString(com.android.internal.R.string.fingerprint_name_template, fingerId);
+ }
+
+ private static File getFileForUser(int userId) {
+ return new File(Environment.getUserSystemDirectory(userId), FINGERPRINT_FILE);
+ }
+
+ private final Runnable mWriteStateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ doWriteState();
+ }
+ };
+
+ private void scheduleWriteStateLocked() {
+ AsyncTask.execute(mWriteStateRunnable);
+ }
+
+ private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
+ ArrayList<Fingerprint> result = new ArrayList<>(array.size());
+ for (int i = 0; i < array.size(); i++) {
+ Fingerprint fp = array.get(i);
+ result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),
+ fp.getDeviceId()));
+ }
+ return result;
+ }
+
+ private void doWriteState() {
+ AtomicFile destination = new AtomicFile(mFile);
+
+ ArrayList<Fingerprint> fingerprints;
+
+ synchronized (this) {
+ fingerprints = getCopy(mFingerprints);
+ }
+
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_FINGERPRINTS);
+
+ final int count = fingerprints.size();
+ for (int i = 0; i < count; i++) {
+ Fingerprint fp = fingerprints.get(i);
+ serializer.startTag(null, TAG_FINGERPRINT);
+ serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getFingerId()));
+ serializer.attribute(null, ATTR_NAME, fp.getName().toString());
+ serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
+ serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
+ serializer.endTag(null, TAG_FINGERPRINT);
+ }
+
+ serializer.endTag(null, TAG_FINGERPRINTS);
+ serializer.endDocument();
+ destination.finishWrite(out);
+
+ // Any error while writing is fatal.
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+ destination.failWrite(out);
+ throw new IllegalStateException("Failed to write fingerprints", t);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private void readStateSyncLocked() {
+ FileInputStream in;
+ if (!mFile.exists()) {
+ return;
+ }
+ try {
+ in = new FileInputStream(mFile);
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(TAG, "No fingerprint state");
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseStateLocked(parser);
+
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed parsing settings file: "
+ + mFile , e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void parseStateLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_FINGERPRINTS)) {
+ parseFingerprintsLocked(parser);
+ }
+ }
+ }
+
+ private void parseFingerprintsLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_FINGERPRINT)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
+ String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
+ String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+ mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId),
+ Integer.parseInt(fingerId), Integer.parseInt(deviceId)));
+ }
+ }
+ }
+
+}