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     @GuardedBy("this")
    206     private void readStateSyncLocked() {
    207         FileInputStream in;
    208         if (!mFile.exists()) {
    209             return;
    210         }
    211         try {
    212             in = new FileInputStream(mFile);
    213         } catch (FileNotFoundException fnfe) {
    214             Slog.i(TAG, "No fingerprint state");
    215             return;
    216         }
    217         try {
    218             XmlPullParser parser = Xml.newPullParser();
    219             parser.setInput(in, null);
    220             parseStateLocked(parser);
    221 
    222         } catch (XmlPullParserException | IOException e) {
    223             throw new IllegalStateException("Failed parsing settings file: "
    224                     + mFile , e);
    225         } finally {
    226             IoUtils.closeQuietly(in);
    227         }
    228     }
    229 
    230     @GuardedBy("this")
    231     private void parseStateLocked(XmlPullParser parser)
    232             throws IOException, XmlPullParserException {
    233         final int outerDepth = parser.getDepth();
    234         int type;
    235         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    236                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    237             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    238                 continue;
    239             }
    240 
    241             String tagName = parser.getName();
    242             if (tagName.equals(TAG_FINGERPRINTS)) {
    243                 parseFingerprintsLocked(parser);
    244             }
    245         }
    246     }
    247 
    248     @GuardedBy("this")
    249     private void parseFingerprintsLocked(XmlPullParser parser)
    250             throws IOException, XmlPullParserException {
    251 
    252         final int outerDepth = parser.getDepth();
    253         int type;
    254         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    255                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    256             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    257                 continue;
    258             }
    259 
    260             String tagName = parser.getName();
    261             if (tagName.equals(TAG_FINGERPRINT)) {
    262                 String name = parser.getAttributeValue(null, ATTR_NAME);
    263                 String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
    264                 String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
    265                 String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
    266                 mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId),
    267                         Integer.parseInt(fingerId), Integer.parseInt(deviceId)));
    268             }
    269         }
    270     }
    271 
    272 }
    273