Home | History | Annotate | Download | only in fingerprint
      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 
     17 package com.android.server.fingerprint;
     18 
     19 import android.content.Context;
     20 import android.hardware.fingerprint.Fingerprint;
     21 import android.os.AsyncTask;
     22 import android.os.Environment;
     23 import android.util.AtomicFile;
     24 import android.util.Slog;
     25 import android.util.Xml;
     26 
     27 import com.android.internal.annotations.GuardedBy;
     28 
     29 import libcore.io.IoUtils;
     30 import org.xmlpull.v1.XmlPullParser;
     31 import org.xmlpull.v1.XmlPullParserException;
     32 import org.xmlpull.v1.XmlSerializer;
     33 
     34 import java.io.File;
     35 import java.io.FileInputStream;
     36 import java.io.FileNotFoundException;
     37 import java.io.FileOutputStream;
     38 import java.io.IOException;
     39 import java.util.ArrayList;
     40 import java.util.List;
     41 
     42 /**
     43  * Class managing the set of fingerprint per user across device reboots.
     44  */
     45 class 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, int groupId) {
     72         synchronized (this) {
     73             mFingerprints.add(new Fingerprint(getUniqueName(), groupId, 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     /**
    111      * Finds a unique name for the given fingerprint
    112      * @return unique name
    113      */
    114     private String getUniqueName() {
    115         int guess = 1;
    116         while (true) {
    117             // Not the most efficient algorithm in the world, but there shouldn't be more than 10
    118             String name = mCtx.getString(com.android.internal.R.string.fingerprint_name_template,
    119                     guess);
    120             if (isUnique(name)) {
    121                 return name;
    122             }
    123             guess++;
    124         }
    125     }
    126 
    127     private boolean isUnique(String name) {
    128         for (Fingerprint fp : mFingerprints) {
    129             if (fp.getName().equals(name)) {
    130                 return false;
    131             }
    132         }
    133         return true;
    134     }
    135 
    136     private static File getFileForUser(int userId) {
    137         return new File(Environment.getUserSystemDirectory(userId), FINGERPRINT_FILE);
    138     }
    139 
    140     private final Runnable mWriteStateRunnable = new Runnable() {
    141         @Override
    142         public void run() {
    143             doWriteState();
    144         }
    145     };
    146 
    147     private void scheduleWriteStateLocked() {
    148         AsyncTask.execute(mWriteStateRunnable);
    149     }
    150 
    151     private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
    152         ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size());
    153         for (int i = 0; i < array.size(); i++) {
    154             Fingerprint fp = array.get(i);
    155             result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),
    156                     fp.getDeviceId()));
    157         }
    158         return result;
    159     }
    160 
    161     private void doWriteState() {
    162         AtomicFile destination = new AtomicFile(mFile);
    163 
    164         ArrayList<Fingerprint> fingerprints;
    165 
    166         synchronized (this) {
    167             fingerprints = getCopy(mFingerprints);
    168         }
    169 
    170         FileOutputStream out = null;
    171         try {
    172             out = destination.startWrite();
    173 
    174             XmlSerializer serializer = Xml.newSerializer();
    175             serializer.setOutput(out, "utf-8");
    176             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    177             serializer.startDocument(null, true);
    178             serializer.startTag(null, TAG_FINGERPRINTS);
    179 
    180             final int count = fingerprints.size();
    181             for (int i = 0; i < count; i++) {
    182                 Fingerprint fp = fingerprints.get(i);
    183                 serializer.startTag(null, TAG_FINGERPRINT);
    184                 serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getFingerId()));
    185                 serializer.attribute(null, ATTR_NAME, fp.getName().toString());
    186                 serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
    187                 serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
    188                 serializer.endTag(null, TAG_FINGERPRINT);
    189             }
    190 
    191             serializer.endTag(null, TAG_FINGERPRINTS);
    192             serializer.endDocument();
    193             destination.finishWrite(out);
    194 
    195             // Any error while writing is fatal.
    196         } catch (Throwable t) {
    197             Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
    198             destination.failWrite(out);
    199             throw new IllegalStateException("Failed to write fingerprints", t);
    200         } finally {
    201             IoUtils.closeQuietly(out);
    202         }
    203     }
    204 
    205     private void readStateSyncLocked() {
    206         FileInputStream in;
    207         if (!mFile.exists()) {
    208             return;
    209         }
    210         try {
    211             in = new FileInputStream(mFile);
    212         } catch (FileNotFoundException fnfe) {
    213             Slog.i(TAG, "No fingerprint state");
    214             return;
    215         }
    216         try {
    217             XmlPullParser parser = Xml.newPullParser();
    218             parser.setInput(in, null);
    219             parseStateLocked(parser);
    220 
    221         } catch (XmlPullParserException | IOException e) {
    222             throw new IllegalStateException("Failed parsing settings file: "
    223                     + mFile , e);
    224         } finally {
    225             IoUtils.closeQuietly(in);
    226         }
    227     }
    228 
    229     private void parseStateLocked(XmlPullParser parser)
    230             throws IOException, XmlPullParserException {
    231         final int outerDepth = parser.getDepth();
    232         int type;
    233         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    234                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    235             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    236                 continue;
    237             }
    238 
    239             String tagName = parser.getName();
    240             if (tagName.equals(TAG_FINGERPRINTS)) {
    241                 parseFingerprintsLocked(parser);
    242             }
    243         }
    244     }
    245 
    246     private void parseFingerprintsLocked(XmlPullParser parser)
    247             throws IOException, XmlPullParserException {
    248 
    249         final int outerDepth = parser.getDepth();
    250         int type;
    251         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    252                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    253             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    254                 continue;
    255             }
    256 
    257             String tagName = parser.getName();
    258             if (tagName.equals(TAG_FINGERPRINT)) {
    259                 String name = parser.getAttributeValue(null, ATTR_NAME);
    260                 String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
    261                 String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
    262                 String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
    263                 mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId),
    264                         Integer.parseInt(fingerId), Integer.parseInt(deviceId)));
    265             }
    266         }
    267     }
    268 
    269 }
    270