FingerprintsUserState.java revision f501b58de8f467a80fef49c704555781bc61ea6f
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.server.fingerprint; 18 19import android.content.Context; 20import android.hardware.fingerprint.Fingerprint; 21import android.os.AsyncTask; 22import android.os.Environment; 23import android.util.AtomicFile; 24import android.util.Slog; 25import android.util.Xml; 26 27import com.android.internal.annotations.GuardedBy; 28 29import libcore.io.IoUtils; 30import org.xmlpull.v1.XmlPullParser; 31import org.xmlpull.v1.XmlPullParserException; 32import org.xmlpull.v1.XmlSerializer; 33 34import java.io.File; 35import java.io.FileInputStream; 36import java.io.FileNotFoundException; 37import java.io.FileOutputStream; 38import java.io.IOException; 39import java.util.ArrayList; 40import java.util.List; 41 42/** 43 * Class managing the set of fingerprint per user across device reboots. 44 */ 45class FingerprintsUserState { 46 47 private static final String TAG = "FingerprintState"; 48 private static final String FINGERPRINT_FILE = "settings_fingerprint.xml"; 49 50 private static final String TAG_FINGERPRINTS = "fingerprints"; 51 private static final String TAG_FINGERPRINT = "fingerprint"; 52 private static final String ATTR_NAME = "name"; 53 private static final String ATTR_GROUP_ID = "groupId"; 54 private static final String ATTR_FINGER_ID = "fingerId"; 55 private static final String ATTR_DEVICE_ID = "deviceId"; 56 57 private final File mFile; 58 59 @GuardedBy("this") 60 private final ArrayList<Fingerprint> mFingerprints = new ArrayList<Fingerprint>(); 61 private final Context mCtx; 62 63 public FingerprintsUserState(Context ctx, int userId) { 64 mFile = getFileForUser(userId); 65 mCtx = ctx; 66 synchronized (this) { 67 readStateSyncLocked(); 68 } 69 } 70 71 public void addFingerprint(int fingerId) { 72 synchronized (this) { 73 mFingerprints.add(new Fingerprint(getDefaultFingerprintName(fingerId), 0, fingerId, 0)); 74 scheduleWriteStateLocked(); 75 } 76 } 77 78 public void removeFingerprint(int fingerId) { 79 synchronized (this) { 80 for (int i = 0; i < mFingerprints.size(); i++) { 81 if (mFingerprints.get(i).getFingerId() == fingerId) { 82 mFingerprints.remove(i); 83 scheduleWriteStateLocked(); 84 break; 85 } 86 } 87 } 88 } 89 90 public void renameFingerprint(int fingerId, CharSequence name) { 91 synchronized (this) { 92 for (int i = 0; i < mFingerprints.size(); i++) { 93 if (mFingerprints.get(i).getFingerId() == fingerId) { 94 Fingerprint old = mFingerprints.get(i); 95 mFingerprints.set(i, new Fingerprint(name, old.getGroupId(), old.getFingerId(), 96 old.getDeviceId())); 97 scheduleWriteStateLocked(); 98 break; 99 } 100 } 101 } 102 } 103 104 public List<Fingerprint> getFingerprints() { 105 synchronized (this) { 106 return getCopy(mFingerprints); 107 } 108 } 109 110 private String getDefaultFingerprintName(int fingerId) { 111 return mCtx.getString(com.android.internal.R.string.fingerprint_name_template, fingerId); 112 } 113 114 private static File getFileForUser(int userId) { 115 return new File(Environment.getUserSystemDirectory(userId), FINGERPRINT_FILE); 116 } 117 118 private final Runnable mWriteStateRunnable = new Runnable() { 119 @Override 120 public void run() { 121 doWriteState(); 122 } 123 }; 124 125 private void scheduleWriteStateLocked() { 126 AsyncTask.execute(mWriteStateRunnable); 127 } 128 129 private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) { 130 ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size()); 131 for (int i = 0; i < array.size(); i++) { 132 Fingerprint fp = array.get(i); 133 result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(), 134 fp.getDeviceId())); 135 } 136 return result; 137 } 138 139 private void doWriteState() { 140 AtomicFile destination = new AtomicFile(mFile); 141 142 ArrayList<Fingerprint> fingerprints; 143 144 synchronized (this) { 145 fingerprints = getCopy(mFingerprints); 146 } 147 148 FileOutputStream out = null; 149 try { 150 out = destination.startWrite(); 151 152 XmlSerializer serializer = Xml.newSerializer(); 153 serializer.setOutput(out, "utf-8"); 154 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 155 serializer.startDocument(null, true); 156 serializer.startTag(null, TAG_FINGERPRINTS); 157 158 final int count = fingerprints.size(); 159 for (int i = 0; i < count; i++) { 160 Fingerprint fp = fingerprints.get(i); 161 serializer.startTag(null, TAG_FINGERPRINT); 162 serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getFingerId())); 163 serializer.attribute(null, ATTR_NAME, fp.getName().toString()); 164 serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId())); 165 serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId())); 166 serializer.endTag(null, TAG_FINGERPRINT); 167 } 168 169 serializer.endTag(null, TAG_FINGERPRINTS); 170 serializer.endDocument(); 171 destination.finishWrite(out); 172 173 // Any error while writing is fatal. 174 } catch (Throwable t) { 175 Slog.wtf(TAG, "Failed to write settings, restoring backup", t); 176 destination.failWrite(out); 177 throw new IllegalStateException("Failed to write fingerprints", t); 178 } finally { 179 IoUtils.closeQuietly(out); 180 } 181 } 182 183 private void readStateSyncLocked() { 184 FileInputStream in; 185 if (!mFile.exists()) { 186 return; 187 } 188 try { 189 in = new FileInputStream(mFile); 190 } catch (FileNotFoundException fnfe) { 191 Slog.i(TAG, "No fingerprint state"); 192 return; 193 } 194 try { 195 XmlPullParser parser = Xml.newPullParser(); 196 parser.setInput(in, null); 197 parseStateLocked(parser); 198 199 } catch (XmlPullParserException | IOException e) { 200 throw new IllegalStateException("Failed parsing settings file: " 201 + mFile , e); 202 } finally { 203 IoUtils.closeQuietly(in); 204 } 205 } 206 207 private void parseStateLocked(XmlPullParser parser) 208 throws IOException, XmlPullParserException { 209 final int outerDepth = parser.getDepth(); 210 int type; 211 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 212 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 213 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 214 continue; 215 } 216 217 String tagName = parser.getName(); 218 if (tagName.equals(TAG_FINGERPRINTS)) { 219 parseFingerprintsLocked(parser); 220 } 221 } 222 } 223 224 private void parseFingerprintsLocked(XmlPullParser parser) 225 throws IOException, XmlPullParserException { 226 227 final int outerDepth = parser.getDepth(); 228 int type; 229 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 230 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 231 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 232 continue; 233 } 234 235 String tagName = parser.getName(); 236 if (tagName.equals(TAG_FINGERPRINT)) { 237 String name = parser.getAttributeValue(null, ATTR_NAME); 238 String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID); 239 String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID); 240 String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID); 241 mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId), 242 Integer.parseInt(fingerId), Integer.parseInt(deviceId))); 243 } 244 } 245 } 246 247} 248