blob: e780ac66cda8937873abe5e286147320bb4ba5c5 [file] [log] [blame]
Gilad Brettercb51b8b2018-03-22 17:04:51 +02001/*
2 * Copyright (C) 2018 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.face;
18
19import android.content.Context;
20import android.hardware.face.Face;
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;
39
40
41/**
42 * Class managing the set of faces per user across device reboots.
43 */
44class FaceUserState {
45
46 private static final String TAG = "FaceState";
47 private static final String FACE_FILE = "settings_face.xml";
48
49 private static final String TAG_FACE = "face";
50 private static final String ATTR_DEVICE_ID = "deviceId";
51
52 private final File mFile;
53
54 @GuardedBy("this")
55 private Face mFace = null;
56 private final Context mCtx;
57
58 public FaceUserState(Context ctx, int userId) {
59 mFile = getFileForUser(userId);
60 mCtx = ctx;
61 synchronized (this) {
62 readStateSyncLocked();
63 }
64 }
65
66 public void addFace(int faceId) {
67 synchronized (this) {
68 mFace = new Face("Face", faceId, 0);
69 scheduleWriteStateLocked();
70 }
71 }
72
73 public void removeFace() {
74 synchronized (this) {
75 mFace = null;
76 scheduleWriteStateLocked();
77 }
78 }
79
80 public Face getFace() {
81 synchronized (this) {
82 return getCopy(mFace);
83 }
84 }
85
86 private static File getFileForUser(int userId) {
87 return new File(Environment.getUserSystemDirectory(userId), FACE_FILE);
88 }
89
90 private final Runnable mWriteStateRunnable = new Runnable() {
91 @Override
92 public void run() {
93 doWriteState();
94 }
95 };
96
97 private void scheduleWriteStateLocked() {
98 AsyncTask.execute(mWriteStateRunnable);
99 }
100
101 private Face getCopy(Face f) {
102 if (f == null) {
103 return null;
104 }
105 return new Face(f.getName(), f.getFaceId(), f.getDeviceId());
106 }
107
108 private void doWriteState() {
109 AtomicFile destination = new AtomicFile(mFile);
110
111 Face face;
112
113 synchronized (this) {
114 face = getCopy(mFace);
115 }
116
117 FileOutputStream out = null;
118 try {
119 out = destination.startWrite();
120
121 XmlSerializer serializer = Xml.newSerializer();
122 serializer.setOutput(out, "utf-8");
123 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
124 serializer.startDocument(null, true);
125 serializer.startTag(null, TAG_FACE);
126 if (face != null) {
127 serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(face.getDeviceId()));
128 }
129 serializer.endTag(null, TAG_FACE);
130 serializer.endDocument();
131 destination.finishWrite(out);
132
133 // Any error while writing is fatal.
134 } catch (Throwable t) {
135 Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
136 destination.failWrite(out);
137 throw new IllegalStateException("Failed to write face", t);
138 } finally {
139 IoUtils.closeQuietly(out);
140 }
141 }
142
143 private void readStateSyncLocked() {
144 FileInputStream in;
145 if (!mFile.exists()) {
146 return;
147 }
148 try {
149 in = new FileInputStream(mFile);
150 } catch (FileNotFoundException fnfe) {
151 Slog.i(TAG, "No face state");
152 return;
153 }
154 try {
155 XmlPullParser parser = Xml.newPullParser();
156 parser.setInput(in, null);
157 parseStateLocked(parser);
158
159 } catch (XmlPullParserException | IOException e) {
160 throw new IllegalStateException("Failed parsing settings file: "
161 + mFile , e);
162 } finally {
163 IoUtils.closeQuietly(in);
164 }
165 }
166
167 private void parseStateLocked(XmlPullParser parser)
168 throws IOException, XmlPullParserException {
169 final int outerDepth = parser.getDepth();
170 int type;
171 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
172 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
173 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
174 continue;
175 }
176
177 String tagName = parser.getName();
178 if (tagName.equals(TAG_FACE)) {
179 parseFaceLocked(parser);
180 }
181 }
182 }
183
184 private void parseFaceLocked(XmlPullParser parser)
185 throws IOException, XmlPullParserException {
186 String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
187
188 mFace = new Face("", 0, Integer.parseInt(deviceId));
189 }
190
191}