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.providers.settings; 18 19 import static android.os.Process.FIRST_APPLICATION_UID; 20 import static android.os.Process.INVALID_UID; 21 22 import android.annotation.NonNull; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManagerInternal; 28 import android.content.pm.Signature; 29 import android.os.Binder; 30 import android.os.Build; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.SystemClock; 35 import android.os.UserHandle; 36 import android.provider.Settings; 37 import android.provider.Settings.Global; 38 import android.providers.settings.SettingsOperationProto; 39 import android.text.TextUtils; 40 import android.util.ArrayMap; 41 import android.util.AtomicFile; 42 import android.util.Base64; 43 import android.util.Slog; 44 import android.util.SparseIntArray; 45 import android.util.StatsLog; 46 import android.util.TimeUtils; 47 import android.util.Xml; 48 import android.util.proto.ProtoOutputStream; 49 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.util.ArrayUtils; 52 import com.android.server.LocalServices; 53 54 import libcore.io.IoUtils; 55 56 import org.xmlpull.v1.XmlPullParser; 57 import org.xmlpull.v1.XmlPullParserException; 58 import org.xmlpull.v1.XmlSerializer; 59 60 import java.io.File; 61 import java.io.FileInputStream; 62 import java.io.FileNotFoundException; 63 import java.io.FileOutputStream; 64 import java.io.IOException; 65 import java.io.PrintWriter; 66 import java.nio.charset.StandardCharsets; 67 import java.nio.file.Files; 68 import java.nio.file.Path; 69 import java.util.ArrayList; 70 import java.util.List; 71 import java.util.Objects; 72 73 /** 74 * This class contains the state for one type of settings. It is responsible 75 * for saving the state asynchronously to an XML file after a mutation and 76 * loading the from an XML file on construction. 77 * <p> 78 * This class uses the same lock as the settings provider to ensure that 79 * multiple changes made by the settings provider, e,g, upgrade, bulk insert, 80 * etc, are atomically persisted since the asynchronous persistence is using 81 * the same lock to grab the current state to write to disk. 82 * </p> 83 */ 84 final class SettingsState { 85 private static final boolean DEBUG = false; 86 private static final boolean DEBUG_PERSISTENCE = false; 87 88 private static final String LOG_TAG = "SettingsState"; 89 90 static final String SYSTEM_PACKAGE_NAME = "android"; 91 92 static final int SETTINGS_VERSION_NEW_ENCODING = 121; 93 94 private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; 95 private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000; 96 97 public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1; 98 public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 20000; 99 100 public static final int VERSION_UNDEFINED = -1; 101 102 private static final String TAG_SETTINGS = "settings"; 103 private static final String TAG_SETTING = "setting"; 104 private static final String ATTR_PACKAGE = "package"; 105 private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet"; 106 private static final String ATTR_TAG = "tag"; 107 private static final String ATTR_TAG_BASE64 = "tagBase64"; 108 109 private static final String ATTR_VERSION = "version"; 110 private static final String ATTR_ID = "id"; 111 private static final String ATTR_NAME = "name"; 112 113 /** 114 * Non-binary value will be written in this attributes. 115 */ 116 private static final String ATTR_VALUE = "value"; 117 private static final String ATTR_DEFAULT_VALUE = "defaultValue"; 118 119 /** 120 * KXmlSerializer won't like some characters. We encode such characters 121 * in base64 and store in this attribute. 122 * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64. 123 */ 124 private static final String ATTR_VALUE_BASE64 = "valueBase64"; 125 private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; 126 127 // This was used in version 120 and before. 128 private static final String NULL_VALUE_OLD_STYLE = "null"; 129 130 private static final int HISTORICAL_OPERATION_COUNT = 20; 131 private static final String HISTORICAL_OPERATION_UPDATE = "update"; 132 private static final String HISTORICAL_OPERATION_DELETE = "delete"; 133 private static final String HISTORICAL_OPERATION_PERSIST = "persist"; 134 private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize"; 135 private static final String HISTORICAL_OPERATION_RESET = "reset"; 136 137 private static final String SHELL_PACKAGE_NAME = "com.android.shell"; 138 private static final String ROOT_PACKAGE_NAME = "root"; 139 140 private static final String NULL_VALUE = "null"; 141 142 private static final Object sLock = new Object(); 143 144 @GuardedBy("sLock") 145 private static final SparseIntArray sSystemUids = new SparseIntArray(); 146 147 @GuardedBy("sLock") 148 private static Signature sSystemSignature; 149 150 private final Object mWriteLock = new Object(); 151 152 private final Object mLock; 153 154 private final Handler mHandler; 155 156 @GuardedBy("mLock") 157 private final Context mContext; 158 159 @GuardedBy("mLock") 160 private final ArrayMap<String, Setting> mSettings = new ArrayMap<>(); 161 162 @GuardedBy("mLock") 163 private final ArrayMap<String, Integer> mPackageToMemoryUsage; 164 165 @GuardedBy("mLock") 166 private final int mMaxBytesPerAppPackage; 167 168 @GuardedBy("mLock") 169 private final File mStatePersistFile; 170 171 @GuardedBy("mLock") 172 private final String mStatePersistTag; 173 174 private final Setting mNullSetting = new Setting(null, null, false, null, null) { 175 @Override 176 public boolean isNull() { 177 return true; 178 } 179 }; 180 181 @GuardedBy("mLock") 182 private final List<HistoricalOperation> mHistoricalOperations; 183 184 @GuardedBy("mLock") 185 public final int mKey; 186 187 @GuardedBy("mLock") 188 private int mVersion = VERSION_UNDEFINED; 189 190 @GuardedBy("mLock") 191 private long mLastNotWrittenMutationTimeMillis; 192 193 @GuardedBy("mLock") 194 private boolean mDirty; 195 196 @GuardedBy("mLock") 197 private boolean mWriteScheduled; 198 199 @GuardedBy("mLock") 200 private long mNextId; 201 202 @GuardedBy("mLock") 203 private int mNextHistoricalOpIdx; 204 205 public static final int SETTINGS_TYPE_GLOBAL = 0; 206 public static final int SETTINGS_TYPE_SYSTEM = 1; 207 public static final int SETTINGS_TYPE_SECURE = 2; 208 public static final int SETTINGS_TYPE_SSAID = 3; 209 public static final int SETTINGS_TYPE_CONFIG = 4; 210 211 public static final int SETTINGS_TYPE_MASK = 0xF0000000; 212 public static final int SETTINGS_TYPE_SHIFT = 28; 213 makeKey(int type, int userId)214 public static int makeKey(int type, int userId) { 215 return (type << SETTINGS_TYPE_SHIFT) | userId; 216 } 217 getTypeFromKey(int key)218 public static int getTypeFromKey(int key) { 219 return key >>> SETTINGS_TYPE_SHIFT; 220 } 221 getUserIdFromKey(int key)222 public static int getUserIdFromKey(int key) { 223 return key & ~SETTINGS_TYPE_MASK; 224 } 225 settingTypeToString(int type)226 public static String settingTypeToString(int type) { 227 switch (type) { 228 case SETTINGS_TYPE_CONFIG: { 229 return "SETTINGS_CONFIG"; 230 } 231 case SETTINGS_TYPE_GLOBAL: { 232 return "SETTINGS_GLOBAL"; 233 } 234 case SETTINGS_TYPE_SECURE: { 235 return "SETTINGS_SECURE"; 236 } 237 case SETTINGS_TYPE_SYSTEM: { 238 return "SETTINGS_SYSTEM"; 239 } 240 case SETTINGS_TYPE_SSAID: { 241 return "SETTINGS_SSAID"; 242 } 243 default: { 244 return "UNKNOWN"; 245 } 246 } 247 } 248 keyToString(int key)249 public static String keyToString(int key) { 250 return "Key[user=" + getUserIdFromKey(key) + ";type=" 251 + settingTypeToString(getTypeFromKey(key)) + "]"; 252 } 253 SettingsState(Context context, Object lock, File file, int key, int maxBytesPerAppPackage, Looper looper)254 public SettingsState(Context context, Object lock, File file, int key, 255 int maxBytesPerAppPackage, Looper looper) { 256 // It is important that we use the same lock as the settings provider 257 // to ensure multiple mutations on this state are atomicaly persisted 258 // as the async persistence should be blocked while we make changes. 259 mContext = context; 260 mLock = lock; 261 mStatePersistFile = file; 262 mStatePersistTag = "settings-" + getTypeFromKey(key) + "-" + getUserIdFromKey(key); 263 mKey = key; 264 mHandler = new MyHandler(looper); 265 if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) { 266 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 267 mPackageToMemoryUsage = new ArrayMap<>(); 268 } else { 269 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 270 mPackageToMemoryUsage = null; 271 } 272 273 mHistoricalOperations = Build.IS_DEBUGGABLE 274 ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; 275 276 synchronized (mLock) { 277 readStateSyncLocked(); 278 } 279 } 280 281 // The settings provider must hold its lock when calling here. 282 @GuardedBy("mLock") getVersionLocked()283 public int getVersionLocked() { 284 return mVersion; 285 } 286 getNullSetting()287 public Setting getNullSetting() { 288 return mNullSetting; 289 } 290 291 // The settings provider must hold its lock when calling here. 292 @GuardedBy("mLock") setVersionLocked(int version)293 public void setVersionLocked(int version) { 294 if (version == mVersion) { 295 return; 296 } 297 mVersion = version; 298 299 scheduleWriteIfNeededLocked(); 300 } 301 302 // The settings provider must hold its lock when calling here. 303 @GuardedBy("mLock") removeSettingsForPackageLocked(String packageName)304 public void removeSettingsForPackageLocked(String packageName) { 305 boolean removedSomething = false; 306 307 final int settingCount = mSettings.size(); 308 for (int i = settingCount - 1; i >= 0; i--) { 309 String name = mSettings.keyAt(i); 310 // Settings defined by us are never dropped. 311 if (Settings.System.PUBLIC_SETTINGS.contains(name) 312 || Settings.System.PRIVATE_SETTINGS.contains(name)) { 313 continue; 314 } 315 Setting setting = mSettings.valueAt(i); 316 if (packageName.equals(setting.packageName)) { 317 mSettings.removeAt(i); 318 removedSomething = true; 319 } 320 } 321 322 if (removedSomething) { 323 scheduleWriteIfNeededLocked(); 324 } 325 } 326 327 // The settings provider must hold its lock when calling here. 328 @GuardedBy("mLock") getSettingNamesLocked()329 public List<String> getSettingNamesLocked() { 330 ArrayList<String> names = new ArrayList<>(); 331 final int settingsCount = mSettings.size(); 332 for (int i = 0; i < settingsCount; i++) { 333 String name = mSettings.keyAt(i); 334 names.add(name); 335 } 336 return names; 337 } 338 339 // The settings provider must hold its lock when calling here. 340 @GuardedBy("mLock") getSettingLocked(String name)341 public Setting getSettingLocked(String name) { 342 if (TextUtils.isEmpty(name)) { 343 return mNullSetting; 344 } 345 Setting setting = mSettings.get(name); 346 if (setting != null) { 347 return new Setting(setting); 348 } 349 return mNullSetting; 350 } 351 352 // The settings provider must hold its lock when calling here. updateSettingLocked(String name, String value, String tag, boolean makeValue, String packageName)353 public boolean updateSettingLocked(String name, String value, String tag, 354 boolean makeValue, String packageName) { 355 if (!hasSettingLocked(name)) { 356 return false; 357 } 358 359 return insertSettingLocked(name, value, tag, makeValue, packageName); 360 } 361 362 // The settings provider must hold its lock when calling here. 363 @GuardedBy("mLock") resetSettingDefaultValueLocked(String name)364 public void resetSettingDefaultValueLocked(String name) { 365 Setting oldSetting = getSettingLocked(name); 366 if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) { 367 String oldValue = oldSetting.getValue(); 368 String oldDefaultValue = oldSetting.getDefaultValue(); 369 Setting newSetting = new Setting(name, oldSetting.getValue(), null, 370 oldSetting.getPackageName(), oldSetting.getTag(), false, 371 oldSetting.getId()); 372 mSettings.put(name, newSetting); 373 updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, 374 newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); 375 scheduleWriteIfNeededLocked(); 376 } 377 } 378 379 // The settings provider must hold its lock when calling here. 380 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, String packageName)381 public boolean insertSettingLocked(String name, String value, String tag, 382 boolean makeDefault, String packageName) { 383 return insertSettingLocked(name, value, tag, makeDefault, false, packageName); 384 } 385 386 // The settings provider must hold its lock when calling here. 387 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName)388 public boolean insertSettingLocked(String name, String value, String tag, 389 boolean makeDefault, boolean forceNonSystemPackage, String packageName) { 390 if (TextUtils.isEmpty(name)) { 391 return false; 392 } 393 394 Setting oldState = mSettings.get(name); 395 String oldValue = (oldState != null) ? oldState.value : null; 396 String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; 397 Setting newState; 398 399 if (oldState != null) { 400 if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) { 401 return false; 402 } 403 newState = oldState; 404 } else { 405 newState = new Setting(name, value, makeDefault, packageName, tag); 406 mSettings.put(name, newState); 407 } 408 409 StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag, 410 makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED); 411 412 addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); 413 414 updateMemoryUsagePerPackageLocked(packageName, oldValue, value, 415 oldDefaultValue, newState.getDefaultValue()); 416 417 scheduleWriteIfNeededLocked(); 418 419 return true; 420 } 421 422 // The settings provider must hold its lock when calling here. persistSyncLocked()423 public void persistSyncLocked() { 424 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 425 doWriteState(); 426 } 427 428 // The settings provider must hold its lock when calling here. 429 @GuardedBy("mLock") deleteSettingLocked(String name)430 public boolean deleteSettingLocked(String name) { 431 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 432 return false; 433 } 434 435 Setting oldState = mSettings.remove(name); 436 437 StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", 438 oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), 439 StatsLog.SETTING_CHANGED__REASON__DELETED); 440 441 updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, 442 null, oldState.defaultValue, null); 443 444 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); 445 446 scheduleWriteIfNeededLocked(); 447 448 return true; 449 } 450 451 // The settings provider must hold its lock when calling here. 452 @GuardedBy("mLock") resetSettingLocked(String name)453 public boolean resetSettingLocked(String name) { 454 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 455 return false; 456 } 457 458 Setting setting = mSettings.get(name); 459 460 Setting oldSetting = new Setting(setting); 461 String oldValue = setting.getValue(); 462 String oldDefaultValue = setting.getDefaultValue(); 463 464 if (!setting.reset()) { 465 return false; 466 } 467 468 String newValue = setting.getValue(); 469 String newDefaultValue = setting.getDefaultValue(); 470 471 updateMemoryUsagePerPackageLocked(setting.packageName, oldValue, 472 newValue, oldDefaultValue, newDefaultValue); 473 474 addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); 475 476 scheduleWriteIfNeededLocked(); 477 478 return true; 479 } 480 481 // The settings provider must hold its lock when calling here. 482 @GuardedBy("mLock") destroyLocked(Runnable callback)483 public void destroyLocked(Runnable callback) { 484 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 485 if (callback != null) { 486 if (mDirty) { 487 // Do it without a delay. 488 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS, 489 callback).sendToTarget(); 490 return; 491 } 492 callback.run(); 493 } 494 } 495 496 @GuardedBy("mLock") addHistoricalOperationLocked(String type, Setting setting)497 private void addHistoricalOperationLocked(String type, Setting setting) { 498 if (mHistoricalOperations == null) { 499 return; 500 } 501 HistoricalOperation operation = new HistoricalOperation( 502 SystemClock.elapsedRealtime(), type, 503 setting != null ? new Setting(setting) : null); 504 if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) { 505 mHistoricalOperations.add(operation); 506 } else { 507 mHistoricalOperations.set(mNextHistoricalOpIdx, operation); 508 } 509 mNextHistoricalOpIdx++; 510 if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) { 511 mNextHistoricalOpIdx = 0; 512 } 513 } 514 515 /** 516 * Dump historical operations as a proto buf. 517 * 518 * @param proto The proto buf stream to dump to 519 * @param fieldId The repeated field ID to use to save an operation to. 520 */ dumpHistoricalOperations(@onNull ProtoOutputStream proto, long fieldId)521 void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) { 522 synchronized (mLock) { 523 if (mHistoricalOperations == null) { 524 return; 525 } 526 527 final int operationCount = mHistoricalOperations.size(); 528 for (int i = 0; i < operationCount; i++) { 529 int index = mNextHistoricalOpIdx - 1 - i; 530 if (index < 0) { 531 index = operationCount + index; 532 } 533 HistoricalOperation operation = mHistoricalOperations.get(index); 534 535 final long token = proto.start(fieldId); 536 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp); 537 proto.write(SettingsOperationProto.OPERATION, operation.mOperation); 538 if (operation.mSetting != null) { 539 // Only add the name of the setting, since we don't know the historical package 540 // and values for it so they would be misleading to add here (all we could 541 // add is what the current data is). 542 proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName()); 543 } 544 proto.end(token); 545 } 546 } 547 } 548 dumpHistoricalOperations(PrintWriter pw)549 public void dumpHistoricalOperations(PrintWriter pw) { 550 synchronized (mLock) { 551 if (mHistoricalOperations == null) { 552 return; 553 } 554 pw.println("Historical operations"); 555 final int operationCount = mHistoricalOperations.size(); 556 for (int i = 0; i < operationCount; i++) { 557 int index = mNextHistoricalOpIdx - 1 - i; 558 if (index < 0) { 559 index = operationCount + index; 560 } 561 HistoricalOperation operation = mHistoricalOperations.get(index); 562 pw.print(TimeUtils.formatForLogging(operation.mTimestamp)); 563 pw.print(" "); 564 pw.print(operation.mOperation); 565 if (operation.mSetting != null) { 566 pw.print(" "); 567 // Only print the name of the setting, since we don't know the 568 // historical package and values for it so they would be misleading 569 // to print here (all we could print is what the current data is). 570 pw.print(operation.mSetting.getName()); 571 } 572 pw.println(); 573 } 574 pw.println(); 575 pw.println(); 576 } 577 } 578 579 @GuardedBy("mLock") updateMemoryUsagePerPackageLocked(String packageName, String oldValue, String newValue, String oldDefaultValue, String newDefaultValue)580 private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, 581 String newValue, String oldDefaultValue, String newDefaultValue) { 582 if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { 583 return; 584 } 585 586 if (SYSTEM_PACKAGE_NAME.equals(packageName)) { 587 return; 588 } 589 590 final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; 591 final int newValueSize = (newValue != null) ? newValue.length() : 0; 592 final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; 593 final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; 594 final int deltaSize = newValueSize + newDefaultValueSize 595 - oldValueSize - oldDefaultValueSize; 596 597 Integer currentSize = mPackageToMemoryUsage.get(packageName); 598 final int newSize = Math.max((currentSize != null) 599 ? currentSize + deltaSize : deltaSize, 0); 600 601 if (newSize > mMaxBytesPerAppPackage) { 602 throw new IllegalStateException("You are adding too many system settings. " 603 + "You should stop using system settings for app specific data" 604 + " package: " + packageName); 605 } 606 607 if (DEBUG) { 608 Slog.i(LOG_TAG, "Settings for package: " + packageName 609 + " size: " + newSize + " bytes."); 610 } 611 612 mPackageToMemoryUsage.put(packageName, newSize); 613 } 614 615 @GuardedBy("mLock") hasSettingLocked(String name)616 private boolean hasSettingLocked(String name) { 617 return mSettings.indexOfKey(name) >= 0; 618 } 619 620 @GuardedBy("mLock") scheduleWriteIfNeededLocked()621 private void scheduleWriteIfNeededLocked() { 622 // If dirty then we have a write already scheduled. 623 if (!mDirty) { 624 mDirty = true; 625 writeStateAsyncLocked(); 626 } 627 } 628 629 @GuardedBy("mLock") writeStateAsyncLocked()630 private void writeStateAsyncLocked() { 631 final long currentTimeMillis = SystemClock.uptimeMillis(); 632 633 if (mWriteScheduled) { 634 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 635 636 // If enough time passed, write without holding off anymore. 637 final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis 638 - mLastNotWrittenMutationTimeMillis; 639 if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) { 640 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget(); 641 return; 642 } 643 644 // Hold off a bit more as settings are frequently changing. 645 final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis 646 + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0); 647 final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis); 648 649 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 650 mHandler.sendMessageDelayed(message, writeDelayMillis); 651 } else { 652 mLastNotWrittenMutationTimeMillis = currentTimeMillis; 653 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 654 mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS); 655 mWriteScheduled = true; 656 } 657 } 658 doWriteState()659 private void doWriteState() { 660 boolean wroteState = false; 661 final int version; 662 final ArrayMap<String, Setting> settings; 663 664 synchronized (mLock) { 665 version = mVersion; 666 settings = new ArrayMap<>(mSettings); 667 mDirty = false; 668 mWriteScheduled = false; 669 } 670 671 synchronized (mWriteLock) { 672 if (DEBUG_PERSISTENCE) { 673 Slog.i(LOG_TAG, "[PERSIST START]"); 674 } 675 676 AtomicFile destination = new AtomicFile(mStatePersistFile, mStatePersistTag); 677 FileOutputStream out = null; 678 try { 679 out = destination.startWrite(); 680 681 XmlSerializer serializer = Xml.newSerializer(); 682 serializer.setOutput(out, StandardCharsets.UTF_8.name()); 683 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", 684 true); 685 serializer.startDocument(null, true); 686 serializer.startTag(null, TAG_SETTINGS); 687 serializer.attribute(null, ATTR_VERSION, String.valueOf(version)); 688 689 final int settingCount = settings.size(); 690 for (int i = 0; i < settingCount; i++) { 691 Setting setting = settings.valueAt(i); 692 693 if (setting.isTransient()) { 694 if (DEBUG_PERSISTENCE) { 695 Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName()); 696 } 697 continue; 698 } 699 700 writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(), 701 setting.getValue(), setting.getDefaultValue(), setting.getPackageName(), 702 setting.getTag(), setting.isDefaultFromSystem()); 703 704 if (DEBUG_PERSISTENCE) { 705 Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" 706 + setting.getValue()); 707 } 708 } 709 710 serializer.endTag(null, TAG_SETTINGS); 711 serializer.endDocument(); 712 destination.finishWrite(out); 713 714 wroteState = true; 715 716 if (DEBUG_PERSISTENCE) { 717 Slog.i(LOG_TAG, "[PERSIST END]"); 718 } 719 } catch (Throwable t) { 720 Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t); 721 if (t instanceof IOException) { 722 // we failed to create a directory, so log the permissions and existence 723 // state for the settings file and directory 724 logSettingsDirectoryInformation(destination.getBaseFile()); 725 if (t.getMessage().contains("Couldn't create directory")) { 726 // attempt to create the directory with Files.createDirectories, which 727 // throws more informative errors than File.mkdirs. 728 Path parentPath = destination.getBaseFile().getParentFile().toPath(); 729 try { 730 Files.createDirectories(parentPath); 731 Slog.i(LOG_TAG, "Successfully created " + parentPath); 732 } catch (Throwable t2) { 733 Slog.e(LOG_TAG, "Failed to write " + parentPath 734 + " with Files.writeDirectories", t2); 735 } 736 } 737 } 738 destination.failWrite(out); 739 } finally { 740 IoUtils.closeQuietly(out); 741 } 742 } 743 744 if (wroteState) { 745 synchronized (mLock) { 746 addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null); 747 } 748 } 749 } 750 logSettingsDirectoryInformation(File settingsFile)751 private static void logSettingsDirectoryInformation(File settingsFile) { 752 File parent = settingsFile.getParentFile(); 753 Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile 754 + " with stacktrace ", new Exception()); 755 File ancestorDir = parent; 756 while (ancestorDir != null) { 757 if (!ancestorDir.exists()) { 758 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 759 + " does not exist"); 760 ancestorDir = ancestorDir.getParentFile(); 761 } else { 762 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 763 + " exists"); 764 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 765 + " permissions: r: " + ancestorDir.canRead() + " w: " 766 + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute()); 767 File ancestorParent = ancestorDir.getParentFile(); 768 if (ancestorParent != null) { 769 Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent 770 + " permissions: r: " + ancestorParent.canRead() + " w: " 771 + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute()); 772 } 773 break; 774 } 775 } 776 } 777 writeSingleSetting(int version, XmlSerializer serializer, String id, String name, String value, String defaultValue, String packageName, String tag, boolean defaultSysSet)778 static void writeSingleSetting(int version, XmlSerializer serializer, String id, 779 String name, String value, String defaultValue, String packageName, 780 String tag, boolean defaultSysSet) throws IOException { 781 if (id == null || isBinary(id) || name == null || isBinary(name) 782 || packageName == null || isBinary(packageName)) { 783 // This shouldn't happen. 784 return; 785 } 786 serializer.startTag(null, TAG_SETTING); 787 serializer.attribute(null, ATTR_ID, id); 788 serializer.attribute(null, ATTR_NAME, name); 789 setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64, 790 version, serializer, value); 791 serializer.attribute(null, ATTR_PACKAGE, packageName); 792 if (defaultValue != null) { 793 setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64, 794 version, serializer, defaultValue); 795 serializer.attribute(null, ATTR_DEFAULT_SYS_SET, Boolean.toString(defaultSysSet)); 796 setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64, 797 version, serializer, tag); 798 } 799 serializer.endTag(null, TAG_SETTING); 800 } 801 setValueAttribute(String attr, String attrBase64, int version, XmlSerializer serializer, String value)802 static void setValueAttribute(String attr, String attrBase64, int version, 803 XmlSerializer serializer, String value) throws IOException { 804 if (version >= SETTINGS_VERSION_NEW_ENCODING) { 805 if (value == null) { 806 // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64. 807 } else if (isBinary(value)) { 808 serializer.attribute(null, attrBase64, base64Encode(value)); 809 } else { 810 serializer.attribute(null, attr, value); 811 } 812 } else { 813 // Old encoding. 814 if (value == null) { 815 serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE); 816 } else { 817 serializer.attribute(null, attr, value); 818 } 819 } 820 } 821 getValueAttribute(XmlPullParser parser, String attr, String base64Attr)822 private String getValueAttribute(XmlPullParser parser, String attr, String base64Attr) { 823 if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) { 824 final String value = parser.getAttributeValue(null, attr); 825 if (value != null) { 826 return value; 827 } 828 final String base64 = parser.getAttributeValue(null, base64Attr); 829 if (base64 != null) { 830 return base64Decode(base64); 831 } 832 // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64. 833 return null; 834 } else { 835 // Old encoding. 836 final String stored = parser.getAttributeValue(null, attr); 837 if (NULL_VALUE_OLD_STYLE.equals(stored)) { 838 return null; 839 } else { 840 return stored; 841 } 842 } 843 } 844 845 @GuardedBy("mLock") readStateSyncLocked()846 private void readStateSyncLocked() { 847 FileInputStream in; 848 try { 849 in = new AtomicFile(mStatePersistFile).openRead(); 850 } catch (FileNotFoundException fnfe) { 851 Slog.i(LOG_TAG, "No settings state " + mStatePersistFile); 852 logSettingsDirectoryInformation(mStatePersistFile); 853 addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null); 854 return; 855 } 856 try { 857 XmlPullParser parser = Xml.newPullParser(); 858 parser.setInput(in, StandardCharsets.UTF_8.name()); 859 parseStateLocked(parser); 860 } catch (XmlPullParserException | IOException e) { 861 String message = "Failed parsing settings file: " + mStatePersistFile; 862 Slog.wtf(LOG_TAG, message); 863 throw new IllegalStateException(message, e); 864 } finally { 865 IoUtils.closeQuietly(in); 866 } 867 } 868 869 /** 870 * Uses AtomicFile to check if the file or its backup exists. 871 * @param file The file to check for existence 872 * @return whether the original or backup exist 873 */ stateFileExists(File file)874 public static boolean stateFileExists(File file) { 875 AtomicFile stateFile = new AtomicFile(file); 876 return stateFile.exists(); 877 } 878 parseStateLocked(XmlPullParser parser)879 private void parseStateLocked(XmlPullParser parser) 880 throws IOException, XmlPullParserException { 881 final int outerDepth = parser.getDepth(); 882 int type; 883 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 884 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 885 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 886 continue; 887 } 888 889 String tagName = parser.getName(); 890 if (tagName.equals(TAG_SETTINGS)) { 891 parseSettingsLocked(parser); 892 } 893 } 894 } 895 896 @GuardedBy("mLock") parseSettingsLocked(XmlPullParser parser)897 private void parseSettingsLocked(XmlPullParser parser) 898 throws IOException, XmlPullParserException { 899 900 mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); 901 902 final int outerDepth = parser.getDepth(); 903 int type; 904 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 905 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 906 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 907 continue; 908 } 909 910 String tagName = parser.getName(); 911 if (tagName.equals(TAG_SETTING)) { 912 String id = parser.getAttributeValue(null, ATTR_ID); 913 String name = parser.getAttributeValue(null, ATTR_NAME); 914 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64); 915 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 916 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE, 917 ATTR_DEFAULT_VALUE_BASE64); 918 String tag = null; 919 boolean fromSystem = false; 920 if (defaultValue != null) { 921 fromSystem = Boolean.parseBoolean(parser.getAttributeValue( 922 null, ATTR_DEFAULT_SYS_SET)); 923 tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); 924 } 925 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, 926 fromSystem, id)); 927 928 if (DEBUG_PERSISTENCE) { 929 Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value); 930 } 931 } 932 } 933 } 934 935 private final class MyHandler extends Handler { 936 public static final int MSG_PERSIST_SETTINGS = 1; 937 MyHandler(Looper looper)938 public MyHandler(Looper looper) { 939 super(looper); 940 } 941 942 @Override handleMessage(Message message)943 public void handleMessage(Message message) { 944 switch (message.what) { 945 case MSG_PERSIST_SETTINGS: { 946 Runnable callback = (Runnable) message.obj; 947 doWriteState(); 948 if (callback != null) { 949 callback.run(); 950 } 951 } 952 break; 953 } 954 } 955 } 956 957 private class HistoricalOperation { 958 final long mTimestamp; 959 final String mOperation; 960 final Setting mSetting; 961 HistoricalOperation(long timestamp, String operation, Setting setting)962 public HistoricalOperation(long timestamp, 963 String operation, Setting setting) { 964 mTimestamp = timestamp; 965 mOperation = operation; 966 mSetting = setting; 967 } 968 } 969 970 class Setting { 971 private String name; 972 private String value; 973 private String defaultValue; 974 private String packageName; 975 private String id; 976 private String tag; 977 // Whether the default is set by the system 978 private boolean defaultFromSystem; 979 Setting(Setting other)980 public Setting(Setting other) { 981 name = other.name; 982 value = other.value; 983 defaultValue = other.defaultValue; 984 packageName = other.packageName; 985 id = other.id; 986 defaultFromSystem = other.defaultFromSystem; 987 tag = other.tag; 988 } 989 Setting(String name, String value, boolean makeDefault, String packageName, String tag)990 public Setting(String name, String value, boolean makeDefault, String packageName, 991 String tag) { 992 this.name = name; 993 update(value, makeDefault, packageName, tag, false); 994 } 995 Setting(String name, String value, String defaultValue, String packageName, String tag, boolean fromSystem, String id)996 public Setting(String name, String value, String defaultValue, 997 String packageName, String tag, boolean fromSystem, String id) { 998 mNextId = Math.max(mNextId, Long.parseLong(id) + 1); 999 if (NULL_VALUE.equals(value)) { 1000 value = null; 1001 } 1002 init(name, value, tag, defaultValue, packageName, fromSystem, id); 1003 } 1004 init(String name, String value, String tag, String defaultValue, String packageName, boolean fromSystem, String id)1005 private void init(String name, String value, String tag, String defaultValue, 1006 String packageName, boolean fromSystem, String id) { 1007 this.name = name; 1008 this.value = value; 1009 this.tag = tag; 1010 this.defaultValue = defaultValue; 1011 this.packageName = packageName; 1012 this.id = id; 1013 this.defaultFromSystem = fromSystem; 1014 } 1015 getName()1016 public String getName() { 1017 return name; 1018 } 1019 getKey()1020 public int getKey() { 1021 return mKey; 1022 } 1023 getValue()1024 public String getValue() { 1025 return value; 1026 } 1027 getTag()1028 public String getTag() { 1029 return tag; 1030 } 1031 getDefaultValue()1032 public String getDefaultValue() { 1033 return defaultValue; 1034 } 1035 getPackageName()1036 public String getPackageName() { 1037 return packageName; 1038 } 1039 isDefaultFromSystem()1040 public boolean isDefaultFromSystem() { 1041 return defaultFromSystem; 1042 } 1043 getId()1044 public String getId() { 1045 return id; 1046 } 1047 isNull()1048 public boolean isNull() { 1049 return false; 1050 } 1051 1052 /** @return whether the value changed */ reset()1053 public boolean reset() { 1054 return update(this.defaultValue, false, packageName, null, true); 1055 } 1056 isTransient()1057 public boolean isTransient() { 1058 switch (getTypeFromKey(getKey())) { 1059 case SETTINGS_TYPE_GLOBAL: 1060 return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName()); 1061 } 1062 return false; 1063 } 1064 update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage)1065 public boolean update(String value, boolean setDefault, String packageName, String tag, 1066 boolean forceNonSystemPackage) { 1067 if (NULL_VALUE.equals(value)) { 1068 value = null; 1069 } 1070 1071 final boolean callerSystem = !forceNonSystemPackage && 1072 !isNull() && isSystemPackage(mContext, packageName); 1073 // Settings set by the system are always defaults. 1074 if (callerSystem) { 1075 setDefault = true; 1076 } 1077 1078 String defaultValue = this.defaultValue; 1079 boolean defaultFromSystem = this.defaultFromSystem; 1080 if (setDefault) { 1081 if (!Objects.equals(value, this.defaultValue) 1082 && (!defaultFromSystem || callerSystem)) { 1083 defaultValue = value; 1084 // Default null means no default, so the tag is irrelevant 1085 // since it is used to reset a settings subset their defaults. 1086 // Also it is irrelevant if the system set the canonical default. 1087 if (defaultValue == null) { 1088 tag = null; 1089 defaultFromSystem = false; 1090 } 1091 } 1092 if (!defaultFromSystem && value != null) { 1093 if (callerSystem) { 1094 defaultFromSystem = true; 1095 } 1096 } 1097 } 1098 1099 // Is something gonna change? 1100 if (Objects.equals(value, this.value) 1101 && Objects.equals(defaultValue, this.defaultValue) 1102 && Objects.equals(packageName, this.packageName) 1103 && Objects.equals(tag, this.tag) 1104 && defaultFromSystem == this.defaultFromSystem) { 1105 return false; 1106 } 1107 1108 init(name, value, tag, defaultValue, packageName, defaultFromSystem, 1109 String.valueOf(mNextId++)); 1110 return true; 1111 } 1112 toString()1113 public String toString() { 1114 return "Setting{name=" + name + " value=" + value 1115 + (defaultValue != null ? " default=" + defaultValue : "") 1116 + " packageName=" + packageName + " tag=" + tag 1117 + " defaultFromSystem=" + defaultFromSystem + "}"; 1118 } 1119 } 1120 1121 /** 1122 * @return TRUE if a string is considered "binary" from KXML's point of view. NOTE DO NOT 1123 * pass null. 1124 */ isBinary(String s)1125 public static boolean isBinary(String s) { 1126 if (s == null) { 1127 throw new NullPointerException(); 1128 } 1129 // See KXmlSerializer.writeEscaped 1130 for (int i = 0; i < s.length(); i++) { 1131 char c = s.charAt(i); 1132 boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 1133 if (!allowedInXml) { 1134 return true; 1135 } 1136 } 1137 return false; 1138 } 1139 base64Encode(String s)1140 private static String base64Encode(String s) { 1141 return Base64.encodeToString(toBytes(s), Base64.NO_WRAP); 1142 } 1143 base64Decode(String s)1144 private static String base64Decode(String s) { 1145 return fromBytes(Base64.decode(s, Base64.DEFAULT)); 1146 } 1147 1148 // Note the followings are basically just UTF-16 encode/decode. But we want to preserve 1149 // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves, 1150 // since I don't know how Charset would treat them. 1151 toBytes(String s)1152 private static byte[] toBytes(String s) { 1153 final byte[] result = new byte[s.length() * 2]; 1154 int resultIndex = 0; 1155 for (int i = 0; i < s.length(); ++i) { 1156 char ch = s.charAt(i); 1157 result[resultIndex++] = (byte) (ch >> 8); 1158 result[resultIndex++] = (byte) ch; 1159 } 1160 return result; 1161 } 1162 fromBytes(byte[] bytes)1163 private static String fromBytes(byte[] bytes) { 1164 final StringBuffer sb = new StringBuffer(bytes.length / 2); 1165 1166 final int last = bytes.length - 1; 1167 1168 for (int i = 0; i < last; i += 2) { 1169 final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff)); 1170 sb.append(ch); 1171 } 1172 return sb.toString(); 1173 } 1174 1175 // Check if a specific package belonging to the caller is part of the system package. isSystemPackage(Context context, String packageName)1176 public static boolean isSystemPackage(Context context, String packageName) { 1177 final int callingUid = Binder.getCallingUid(); 1178 final int callingUserId = UserHandle.getUserId(callingUid); 1179 return isSystemPackage(context, packageName, callingUid, callingUserId); 1180 } 1181 1182 // Check if a specific package, uid, and user ID are part of the system package. isSystemPackage(Context context, String packageName, int uid, int userId)1183 public static boolean isSystemPackage(Context context, String packageName, int uid, 1184 int userId) { 1185 synchronized (sLock) { 1186 if (SYSTEM_PACKAGE_NAME.equals(packageName)) { 1187 return true; 1188 } 1189 1190 // Shell and Root are not considered a part of the system 1191 if (SHELL_PACKAGE_NAME.equals(packageName) 1192 || ROOT_PACKAGE_NAME.equals(packageName)) { 1193 return false; 1194 } 1195 1196 if (uid != INVALID_UID) { 1197 // Native services running as a special UID get a pass 1198 final int callingAppId = UserHandle.getAppId(uid); 1199 if (callingAppId < FIRST_APPLICATION_UID) { 1200 sSystemUids.put(callingAppId, callingAppId); 1201 return true; 1202 } 1203 } 1204 1205 final long identity = Binder.clearCallingIdentity(); 1206 try { 1207 try { 1208 uid = context.getPackageManager().getPackageUidAsUser(packageName, 0, userId); 1209 } catch (PackageManager.NameNotFoundException e) { 1210 return false; 1211 } 1212 1213 // If the system or a special system UID (like telephony), done. 1214 if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) { 1215 sSystemUids.put(uid, uid); 1216 return true; 1217 } 1218 1219 // If already known system component, done. 1220 if (sSystemUids.indexOfKey(uid) >= 0) { 1221 return true; 1222 } 1223 1224 // If SetupWizard, done. 1225 PackageManagerInternal packageManagerInternal = LocalServices.getService( 1226 PackageManagerInternal.class); 1227 if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) { 1228 sSystemUids.put(uid, uid); 1229 return true; 1230 } 1231 1232 // If a persistent system app, done. 1233 PackageInfo packageInfo; 1234 try { 1235 packageInfo = context.getPackageManager().getPackageInfoAsUser( 1236 packageName, PackageManager.GET_SIGNATURES, userId); 1237 if ((packageInfo.applicationInfo.flags 1238 & ApplicationInfo.FLAG_PERSISTENT) != 0 1239 && (packageInfo.applicationInfo.flags 1240 & ApplicationInfo.FLAG_SYSTEM) != 0) { 1241 sSystemUids.put(uid, uid); 1242 return true; 1243 } 1244 } catch (PackageManager.NameNotFoundException e) { 1245 return false; 1246 } 1247 1248 // Last check if system signed. 1249 if (sSystemSignature == null) { 1250 try { 1251 sSystemSignature = context.getPackageManager().getPackageInfoAsUser( 1252 SYSTEM_PACKAGE_NAME, PackageManager.GET_SIGNATURES, 1253 UserHandle.USER_SYSTEM).signatures[0]; 1254 } catch (PackageManager.NameNotFoundException e) { 1255 /* impossible */ 1256 return false; 1257 } 1258 } 1259 if (sSystemSignature.equals(packageInfo.signatures[0])) { 1260 sSystemUids.put(uid, uid); 1261 return true; 1262 } 1263 } finally { 1264 Binder.restoreCallingIdentity(identity); 1265 } 1266 1267 return false; 1268 } 1269 } 1270 } 1271