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