1 /*
2  * Copyright (C) 2021 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.sensorprivacy;
18 
19 import android.hardware.SensorPrivacyManager;
20 import android.os.Environment;
21 import android.os.UserHandle;
22 import android.service.SensorPrivacyIndividualEnabledSensorProto;
23 import android.service.SensorPrivacySensorProto;
24 import android.service.SensorPrivacyServiceDumpProto;
25 import android.service.SensorPrivacyUserProto;
26 import android.util.ArrayMap;
27 import android.util.AtomicFile;
28 import android.util.Log;
29 import android.util.Pair;
30 import android.util.SparseArray;
31 import android.util.Xml;
32 
33 import com.android.internal.util.XmlUtils;
34 import com.android.internal.util.dump.DualDumpOutputStream;
35 import com.android.internal.util.function.QuadConsumer;
36 import com.android.internal.util.function.pooled.PooledLambda;
37 import com.android.modules.utils.TypedXmlPullParser;
38 import com.android.modules.utils.TypedXmlSerializer;
39 import com.android.server.IoThread;
40 import com.android.server.LocalServices;
41 import com.android.server.pm.UserManagerInternal;
42 
43 import org.xmlpull.v1.XmlPullParser;
44 import org.xmlpull.v1.XmlPullParserException;
45 
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileOutputStream;
49 import java.io.IOException;
50 
51 /**
52  * Class for managing persisted state. Synchronization must be handled by the caller.
53  */
54 class PersistedState {
55 
56     private static final String LOG_TAG = PersistedState.class.getSimpleName();
57 
58     /** Version number indicating compatibility parsing the persisted file */
59     private static final int CURRENT_PERSISTENCE_VERSION = 2;
60     /** Version number indicating the persisted data needs upgraded to match new internal data
61      *  structures and features */
62     private static final int CURRENT_VERSION = 2;
63 
64     private static final String XML_TAG_SENSOR_PRIVACY = "sensor-privacy";
65     private static final String XML_TAG_SENSOR_STATE = "sensor-state";
66     private static final String XML_ATTRIBUTE_PERSISTENCE_VERSION = "persistence-version";
67     private static final String XML_ATTRIBUTE_VERSION = "version";
68     private static final String XML_ATTRIBUTE_TOGGLE_TYPE = "toggle-type";
69     private static final String XML_ATTRIBUTE_USER_ID = "user-id";
70     private static final String XML_ATTRIBUTE_SENSOR = "sensor";
71     private static final String XML_ATTRIBUTE_STATE_TYPE = "state-type";
72     private static final String XML_ATTRIBUTE_LAST_CHANGE = "last-change";
73 
74     private final AtomicFile mAtomicFile;
75 
76     private ArrayMap<TypeUserSensor, SensorState> mStates = new ArrayMap<>();
77 
fromFile(String fileName)78     static PersistedState fromFile(String fileName) {
79         return new PersistedState(fileName);
80     }
81 
PersistedState(String fileName)82     private PersistedState(String fileName) {
83         mAtomicFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), fileName));
84         readState();
85     }
86 
readState()87     private void readState() {
88         AtomicFile file = mAtomicFile;
89         if (!file.exists()) {
90             AtomicFile fileToMigrateFrom =
91                     new AtomicFile(new File(Environment.getDataSystemDirectory(),
92                             "sensor_privacy.xml"));
93 
94             if (fileToMigrateFrom.exists()) {
95                 // Sample the start tag to determine if migration is needed
96                 try (FileInputStream inputStream = fileToMigrateFrom.openRead()) {
97                     TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
98                     XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
99                     file = fileToMigrateFrom;
100                 } catch (IOException e) {
101                     Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
102                     // Delete the file to prevent the same error on subsequent calls and assume
103                     // sensor privacy is not enabled.
104                     fileToMigrateFrom.delete();
105                 } catch (XmlPullParserException e) {
106                     // No migration needed
107                 }
108             }
109         }
110 
111         Object nonupgradedState = null;
112         if (file.exists()) {
113             try (FileInputStream inputStream = file.openRead()) {
114                 TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
115                 XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
116                 final int persistenceVersion = parser.getAttributeInt(null,
117                         XML_ATTRIBUTE_PERSISTENCE_VERSION, 0);
118 
119                 // Use inline string literals for xml tags/attrs when parsing old versions since
120                 // these should never be changed even with refactorings.
121                 if (persistenceVersion == 0) {
122                     int version = 0;
123                     PVersion0 version0 = new PVersion0(version);
124                     nonupgradedState = version0;
125                     readPVersion0(parser, version0);
126                 } else if (persistenceVersion == 1) {
127                     int version = parser.getAttributeInt(null,
128                             "version", 1);
129                     PVersion1 version1 = new PVersion1(version);
130                     nonupgradedState = version1;
131 
132                     readPVersion1(parser, version1);
133                 } else if (persistenceVersion == CURRENT_PERSISTENCE_VERSION) {
134                     int version = parser.getAttributeInt(null,
135                             XML_ATTRIBUTE_VERSION, 2);
136                     PVersion2 version2 = new PVersion2(version);
137                     nonupgradedState = version2;
138 
139                     readPVersion2(parser, version2);
140                 } else {
141                     Log.e(LOG_TAG, "Unknown persistence version: " + persistenceVersion
142                                     + ". Deleting.",
143                             new RuntimeException());
144                     file.delete();
145                     nonupgradedState = null;
146                 }
147 
148             } catch (IOException | XmlPullParserException | RuntimeException e) {
149                 Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
150                 // Delete the file to prevent the same error on subsequent calls and assume
151                 // sensor privacy is not enabled.
152                 file.delete();
153                 nonupgradedState = null;
154             }
155         }
156 
157         if (nonupgradedState == null) {
158             // New file, default state for current version goes here.
159             nonupgradedState = new PVersion2(2);
160         }
161 
162         if (nonupgradedState instanceof PVersion0) {
163             nonupgradedState = PVersion1.fromPVersion0((PVersion0) nonupgradedState);
164         }
165         if (nonupgradedState instanceof PVersion1) {
166             nonupgradedState = PVersion2.fromPVersion1((PVersion1) nonupgradedState);
167         }
168         if (nonupgradedState instanceof PVersion2) {
169             PVersion2 upgradedState = (PVersion2) nonupgradedState;
170             mStates = upgradedState.mStates;
171         } else {
172             Log.e(LOG_TAG, "State not successfully upgraded.");
173             mStates = new ArrayMap<>();
174         }
175     }
176 
readPVersion0(TypedXmlPullParser parser, PVersion0 version0)177     private static void readPVersion0(TypedXmlPullParser parser, PVersion0 version0)
178             throws XmlPullParserException, IOException {
179 
180         XmlUtils.nextElement(parser);
181         while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
182             if ("individual-sensor-privacy".equals(parser.getName())) {
183                 int sensor = XmlUtils.readIntAttribute(parser, "sensor");
184                 boolean indEnabled = XmlUtils.readBooleanAttribute(parser,
185                         "enabled");
186                 version0.addState(sensor, indEnabled);
187                 XmlUtils.skipCurrentTag(parser);
188             } else {
189                 XmlUtils.nextElement(parser);
190             }
191         }
192     }
193 
readPVersion1(TypedXmlPullParser parser, PVersion1 version1)194     private static void readPVersion1(TypedXmlPullParser parser, PVersion1 version1)
195             throws XmlPullParserException, IOException {
196         while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
197             XmlUtils.nextElement(parser);
198 
199             if ("user".equals(parser.getName())) {
200                 int currentUserId = parser.getAttributeInt(null, "id");
201                 int depth = parser.getDepth();
202                 while (XmlUtils.nextElementWithin(parser, depth)) {
203                     if ("individual-sensor-privacy".equals(parser.getName())) {
204                         int sensor = parser.getAttributeInt(null, "sensor");
205                         boolean isEnabled = parser.getAttributeBoolean(null,
206                                 "enabled");
207                         version1.addState(currentUserId, sensor, isEnabled);
208                     }
209                 }
210             }
211         }
212     }
213 
readPVersion2(TypedXmlPullParser parser, PVersion2 version2)214     private static void readPVersion2(TypedXmlPullParser parser, PVersion2 version2)
215             throws XmlPullParserException, IOException {
216 
217         while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
218             XmlUtils.nextElement(parser);
219 
220             if (XML_TAG_SENSOR_STATE.equals(parser.getName())) {
221                 int toggleType = parser.getAttributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE);
222                 int userId = parser.getAttributeInt(null, XML_ATTRIBUTE_USER_ID);
223                 int sensor = parser.getAttributeInt(null, XML_ATTRIBUTE_SENSOR);
224                 int state = parser.getAttributeInt(null, XML_ATTRIBUTE_STATE_TYPE);
225                 long lastChange = parser.getAttributeLong(null, XML_ATTRIBUTE_LAST_CHANGE);
226 
227                 version2.addState(toggleType, userId, sensor, state, lastChange);
228             } else {
229                 XmlUtils.skipCurrentTag(parser);
230             }
231         }
232     }
233 
getState(int toggleType, int userId, int sensor)234     public SensorState getState(int toggleType, int userId, int sensor) {
235         return mStates.get(new TypeUserSensor(toggleType, userId, sensor));
236     }
237 
setState(int toggleType, int userId, int sensor, SensorState sensorState)238     public SensorState setState(int toggleType, int userId, int sensor, SensorState sensorState) {
239         return mStates.put(new TypeUserSensor(toggleType, userId, sensor), sensorState);
240     }
241 
242     private static class TypeUserSensor {
243 
244         int mType;
245         int mUserId;
246         int mSensor;
247 
TypeUserSensor(int type, int userId, int sensor)248         TypeUserSensor(int type, int userId, int sensor) {
249             mType = type;
250             mUserId = userId;
251             mSensor = sensor;
252         }
253 
TypeUserSensor(TypeUserSensor typeUserSensor)254         TypeUserSensor(TypeUserSensor typeUserSensor) {
255             this(typeUserSensor.mType, typeUserSensor.mUserId, typeUserSensor.mSensor);
256         }
257 
258         @Override
equals(Object o)259         public boolean equals(Object o) {
260             if (this == o) return true;
261             if (!(o instanceof TypeUserSensor)) return false;
262             TypeUserSensor that = (TypeUserSensor) o;
263             return mType == that.mType && mUserId == that.mUserId && mSensor == that.mSensor;
264         }
265 
266         @Override
hashCode()267         public int hashCode() {
268             return 31 * (31 * mType + mUserId) + mSensor;
269         }
270     }
271 
schedulePersist()272     void schedulePersist() {
273         int numStates = mStates.size();
274 
275         ArrayMap<TypeUserSensor, SensorState> statesCopy = new ArrayMap<>();
276         for (int i = 0; i < numStates; i++) {
277             statesCopy.put(new TypeUserSensor(mStates.keyAt(i)),
278                     new SensorState(mStates.valueAt(i)));
279         }
280         IoThread.getHandler().sendMessage(
281                 PooledLambda.obtainMessage(PersistedState::persist, this, statesCopy));
282     }
283 
persist(ArrayMap<TypeUserSensor, SensorState> states)284     private void persist(ArrayMap<TypeUserSensor, SensorState> states) {
285         FileOutputStream outputStream = null;
286         try {
287             outputStream = mAtomicFile.startWrite();
288             TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
289             serializer.startDocument(null, true);
290             serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
291             serializer.attributeInt(null, XML_ATTRIBUTE_PERSISTENCE_VERSION,
292                     CURRENT_PERSISTENCE_VERSION);
293             serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, CURRENT_VERSION);
294             for (int i = 0; i < states.size(); i++) {
295                 TypeUserSensor userSensor = states.keyAt(i);
296                 SensorState sensorState = states.valueAt(i);
297 
298                 // Do not persist hardware toggle states. Will be restored on reboot
299                 if (userSensor.mType != SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
300                     continue;
301                 }
302 
303                 serializer.startTag(null, XML_TAG_SENSOR_STATE);
304                 serializer.attributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE,
305                         userSensor.mType);
306                 serializer.attributeInt(null, XML_ATTRIBUTE_USER_ID,
307                         userSensor.mUserId);
308                 serializer.attributeInt(null, XML_ATTRIBUTE_SENSOR,
309                         userSensor.mSensor);
310                 serializer.attributeInt(null, XML_ATTRIBUTE_STATE_TYPE,
311                         sensorState.getState());
312                 serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CHANGE,
313                         sensorState.getLastChange());
314                 serializer.endTag(null, XML_TAG_SENSOR_STATE);
315             }
316 
317             serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
318             serializer.endDocument();
319             mAtomicFile.finishWrite(outputStream);
320         } catch (IOException e) {
321             Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
322             mAtomicFile.failWrite(outputStream);
323         }
324     }
325 
dump(DualDumpOutputStream dumpStream)326     void dump(DualDumpOutputStream dumpStream) {
327         // Collect per user, then per sensor. <toggle type, state>
328         SparseArray<SparseArray<Pair<Integer, SensorState>>> statesMatrix = new SparseArray<>();
329         int numStates = mStates.size();
330         for (int i = 0; i < numStates; i++) {
331             int toggleType = mStates.keyAt(i).mType;
332             int userId = mStates.keyAt(i).mUserId;
333             int sensor = mStates.keyAt(i).mSensor;
334 
335             SparseArray<Pair<Integer, SensorState>> userStates = statesMatrix.get(userId);
336             if (userStates == null) {
337                 userStates = new SparseArray<>();
338                 statesMatrix.put(userId, userStates);
339             }
340             userStates.put(sensor, new Pair<>(toggleType, mStates.valueAt(i)));
341         }
342 
343         dumpStream.write("storage_implementation",
344                 SensorPrivacyServiceDumpProto.STORAGE_IMPLEMENTATION,
345                 SensorPrivacyStateControllerImpl.class.getName());
346 
347         int numUsers = statesMatrix.size();
348         for (int i = 0; i < numUsers; i++) {
349             int userId = statesMatrix.keyAt(i);
350             long userToken = dumpStream.start("users", SensorPrivacyServiceDumpProto.USER);
351             dumpStream.write("user_id", SensorPrivacyUserProto.USER_ID, userId);
352             SparseArray<Pair<Integer, SensorState>> userStates = statesMatrix.valueAt(i);
353             int numSensors = userStates.size();
354             for (int j = 0; j < numSensors; j++) {
355                 int sensor = userStates.keyAt(j);
356                 int toggleType = userStates.valueAt(j).first;
357                 SensorState sensorState = userStates.valueAt(j).second;
358                 long sensorToken = dumpStream.start("sensors", SensorPrivacyUserProto.SENSORS);
359                 dumpStream.write("sensor", SensorPrivacySensorProto.SENSOR, sensor);
360                 long toggleToken = dumpStream.start("toggles", SensorPrivacySensorProto.TOGGLES);
361                 dumpStream.write("toggle_type",
362                         SensorPrivacyIndividualEnabledSensorProto.TOGGLE_TYPE,
363                         toggleType);
364                 dumpStream.write("state_type",
365                         SensorPrivacyIndividualEnabledSensorProto.STATE_TYPE,
366                         sensorState.getState());
367                 dumpStream.write("last_change",
368                         SensorPrivacyIndividualEnabledSensorProto.LAST_CHANGE,
369                         sensorState.getLastChange());
370                 dumpStream.end(toggleToken);
371                 dumpStream.end(sensorToken);
372             }
373             dumpStream.end(userToken);
374         }
375     }
376 
forEachKnownState(QuadConsumer<Integer, Integer, Integer, SensorState> consumer)377     void forEachKnownState(QuadConsumer<Integer, Integer, Integer, SensorState> consumer) {
378         int numStates = mStates.size();
379         for (int i = 0; i < numStates; i++) {
380             TypeUserSensor tus = mStates.keyAt(i);
381             SensorState sensorState = mStates.valueAt(i);
382             consumer.accept(tus.mType, tus.mUserId, tus.mSensor, sensorState);
383         }
384     }
385 
386     // Structure for persistence version 0
387     private static class PVersion0 {
388         private SparseArray<SensorState> mIndividualEnabled = new SparseArray<>();
389 
PVersion0(int version)390         private PVersion0(int version) {
391             if (version != 0) {
392                 throw new RuntimeException("Only version 0 supported");
393             }
394         }
395 
addState(int sensor, boolean enabled)396         private void addState(int sensor, boolean enabled) {
397             mIndividualEnabled.put(sensor, new SensorState(enabled));
398         }
399 
upgrade()400         private void upgrade() {
401             // No op, only version 0 is supported
402         }
403     }
404 
405     // Structure for persistence version 1
406     private static class PVersion1 {
407         private SparseArray<SparseArray<SensorState>> mIndividualEnabled = new SparseArray<>();
408 
PVersion1(int version)409         private PVersion1(int version) {
410             if (version != 1) {
411                 throw new RuntimeException("Only version 1 supported");
412             }
413         }
414 
fromPVersion0(PVersion0 version0)415         private static PVersion1 fromPVersion0(PVersion0 version0) {
416             version0.upgrade();
417 
418             PVersion1 result = new PVersion1(1);
419 
420             int[] users = {UserHandle.USER_SYSTEM};
421             try {
422                 users = LocalServices.getService(UserManagerInternal.class).getUserIds();
423             } catch (Exception e) {
424                 Log.e(LOG_TAG, "Unable to get users.", e);
425             }
426 
427             // Copy global state to each user
428             for (int i = 0; i < users.length; i++) {
429                 int userId = users[i];
430 
431                 for (int j = 0; j < version0.mIndividualEnabled.size(); j++) {
432                     final int sensor = version0.mIndividualEnabled.keyAt(j);
433                     final SensorState sensorState = version0.mIndividualEnabled.valueAt(j);
434 
435                     result.addState(userId, sensor, sensorState.isEnabled());
436                 }
437             }
438 
439             return result;
440         }
441 
addState(int userId, int sensor, boolean enabled)442         private void addState(int userId, int sensor, boolean enabled) {
443             SparseArray<SensorState> userIndividualSensorEnabled =
444                     mIndividualEnabled.get(userId, new SparseArray<>());
445             mIndividualEnabled.put(userId, userIndividualSensorEnabled);
446 
447             userIndividualSensorEnabled
448                     .put(sensor, new SensorState(enabled));
449         }
450 
upgrade()451         private void upgrade() {
452             // No op, only version 1 is supported
453         }
454     }
455 
456     // Structure for persistence version 2
457     private static class PVersion2 {
458         private ArrayMap<TypeUserSensor, SensorState> mStates = new ArrayMap<>();
459 
PVersion2(int version)460         private PVersion2(int version) {
461             if (version != 2) {
462                 throw new RuntimeException("Only version 2 supported");
463             }
464         }
465 
fromPVersion1(PVersion1 version1)466         private static PVersion2 fromPVersion1(PVersion1 version1) {
467             version1.upgrade();
468 
469             PVersion2 result = new PVersion2(2);
470 
471             SparseArray<SparseArray<SensorState>> individualEnabled =
472                     version1.mIndividualEnabled;
473             int numUsers = individualEnabled.size();
474             for (int i = 0; i < numUsers; i++) {
475                 int userId = individualEnabled.keyAt(i);
476                 SparseArray<SensorState> userIndividualEnabled = individualEnabled.valueAt(i);
477                 int numSensors = userIndividualEnabled.size();
478                 for (int j = 0; j < numSensors; j++) {
479                     int sensor = userIndividualEnabled.keyAt(j);
480                     SensorState sensorState = userIndividualEnabled.valueAt(j);
481                     result.addState(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
482                             userId, sensor, sensorState.getState(), sensorState.getLastChange());
483                 }
484             }
485 
486             return result;
487         }
488 
addState(int toggleType, int userId, int sensor, int state, long lastChange)489         private void addState(int toggleType, int userId, int sensor, int state,
490                 long lastChange) {
491             mStates.put(new TypeUserSensor(toggleType, userId, sensor),
492                     new SensorState(state, lastChange));
493         }
494     }
495 
resetForTesting()496     public void resetForTesting() {
497         mStates = new ArrayMap<>();
498     }
499 }
500