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 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 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.os.Binder; 28 import android.os.Build; 29 import android.os.FileUtils; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.SystemClock; 34 import android.os.UserHandle; 35 import android.provider.Settings; 36 import android.provider.Settings.Global; 37 import android.providers.settings.SettingsOperationProto; 38 import android.text.TextUtils; 39 import android.util.ArrayMap; 40 import android.util.ArraySet; 41 import android.util.AtomicFile; 42 import android.util.Base64; 43 import android.util.Slog; 44 import android.util.TimeUtils; 45 import android.util.Xml; 46 import android.util.proto.ProtoOutputStream; 47 48 import com.android.internal.annotations.GuardedBy; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.util.ArrayUtils; 51 import com.android.internal.util.FrameworkStatsLog; 52 import com.android.modules.utils.TypedXmlPullParser; 53 import com.android.modules.utils.TypedXmlSerializer; 54 55 import libcore.io.IoUtils; 56 57 import org.xmlpull.v1.XmlPullParser; 58 import org.xmlpull.v1.XmlPullParserException; 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.file.Files; 67 import java.nio.file.Path; 68 import java.util.ArrayList; 69 import java.util.HashSet; 70 import java.util.Iterator; 71 import java.util.List; 72 import java.util.Map; 73 import java.util.Objects; 74 import java.util.Set; 75 76 /** 77 * This class contains the state for one type of settings. It is responsible 78 * for saving the state asynchronously to an XML file after a mutation and 79 * loading the from an XML file on construction. 80 * <p> 81 * This class uses the same lock as the settings provider to ensure that 82 * multiple changes made by the settings provider, e,g, upgrade, bulk insert, 83 * etc, are atomically persisted since the asynchronous persistence is using 84 * the same lock to grab the current state to write to disk. 85 * </p> 86 */ 87 final class SettingsState { 88 private static final boolean DEBUG = false; 89 private static final boolean DEBUG_PERSISTENCE = false; 90 91 private static final String LOG_TAG = "SettingsState"; 92 93 static final String SYSTEM_PACKAGE_NAME = "android"; 94 95 static final int SETTINGS_VERSION_NEW_ENCODING = 121; 96 97 public static final int MAX_LENGTH_PER_STRING = 32768; 98 private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; 99 private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000; 100 101 public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1; 102 public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 40000; 103 104 public static final int VERSION_UNDEFINED = -1; 105 106 public static final String FALLBACK_FILE_SUFFIX = ".fallback"; 107 108 private static final String TAG_SETTINGS = "settings"; 109 private static final String TAG_SETTING = "setting"; 110 private static final String ATTR_PACKAGE = "package"; 111 private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet"; 112 private static final String ATTR_TAG = "tag"; 113 private static final String ATTR_TAG_BASE64 = "tagBase64"; 114 115 private static final String ATTR_VERSION = "version"; 116 private static final String ATTR_ID = "id"; 117 private static final String ATTR_NAME = "name"; 118 119 private static final String TAG_NAMESPACE_HASHES = "namespaceHashes"; 120 private static final String TAG_NAMESPACE_HASH = "namespaceHash"; 121 private static final String ATTR_NAMESPACE = "namespace"; 122 private static final String ATTR_BANNED_HASH = "bannedHash"; 123 124 private static final String ATTR_PRESERVE_IN_RESTORE = "preserve_in_restore"; 125 126 /** 127 * Non-binary value will be written in this attributes. 128 */ 129 private static final String ATTR_VALUE = "value"; 130 private static final String ATTR_DEFAULT_VALUE = "defaultValue"; 131 132 /** 133 * KXmlSerializer won't like some characters. We encode such characters 134 * in base64 and store in this attribute. 135 * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64. 136 */ 137 private static final String ATTR_VALUE_BASE64 = "valueBase64"; 138 private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; 139 140 // This was used in version 120 and before. 141 private static final String NULL_VALUE_OLD_STYLE = "null"; 142 143 private static final int HISTORICAL_OPERATION_COUNT = 20; 144 private static final String HISTORICAL_OPERATION_UPDATE = "update"; 145 private static final String HISTORICAL_OPERATION_DELETE = "delete"; 146 private static final String HISTORICAL_OPERATION_PERSIST = "persist"; 147 private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize"; 148 private static final String HISTORICAL_OPERATION_RESET = "reset"; 149 150 private static final String SHELL_PACKAGE_NAME = "com.android.shell"; 151 private static final String ROOT_PACKAGE_NAME = "root"; 152 153 private static final String NULL_VALUE = "null"; 154 155 private static final ArraySet<String> sSystemPackages = new ArraySet<>(); 156 157 private final Object mWriteLock = new Object(); 158 159 private final Object mLock; 160 161 private final Handler mHandler; 162 163 @GuardedBy("mLock") 164 private final Context mContext; 165 166 @GuardedBy("mLock") 167 private final ArrayMap<String, Setting> mSettings = new ArrayMap<>(); 168 169 @GuardedBy("mLock") 170 private final ArrayMap<String, String> mNamespaceBannedHashes = new ArrayMap<>(); 171 172 @GuardedBy("mLock") 173 private final ArrayMap<String, Integer> mPackageToMemoryUsage; 174 175 @GuardedBy("mLock") 176 private final int mMaxBytesPerAppPackage; 177 178 @GuardedBy("mLock") 179 private final File mStatePersistFile; 180 181 @GuardedBy("mLock") 182 private final String mStatePersistTag; 183 184 private final Setting mNullSetting = new Setting(null, null, false, null, null) { 185 @Override 186 public boolean isNull() { 187 return true; 188 } 189 }; 190 191 @GuardedBy("mLock") 192 private final List<HistoricalOperation> mHistoricalOperations; 193 194 @GuardedBy("mLock") 195 public final int mKey; 196 197 @GuardedBy("mLock") 198 private int mVersion = VERSION_UNDEFINED; 199 200 @GuardedBy("mLock") 201 private long mLastNotWrittenMutationTimeMillis; 202 203 @GuardedBy("mLock") 204 private boolean mDirty; 205 206 @GuardedBy("mLock") 207 private boolean mWriteScheduled; 208 209 @GuardedBy("mLock") 210 private long mNextId; 211 212 @GuardedBy("mLock") 213 private int mNextHistoricalOpIdx; 214 215 public static final int SETTINGS_TYPE_GLOBAL = 0; 216 public static final int SETTINGS_TYPE_SYSTEM = 1; 217 public static final int SETTINGS_TYPE_SECURE = 2; 218 public static final int SETTINGS_TYPE_SSAID = 3; 219 public static final int SETTINGS_TYPE_CONFIG = 4; 220 221 public static final int SETTINGS_TYPE_MASK = 0xF0000000; 222 public static final int SETTINGS_TYPE_SHIFT = 28; 223 makeKey(int type, int userId)224 public static int makeKey(int type, int userId) { 225 return (type << SETTINGS_TYPE_SHIFT) | userId; 226 } 227 getTypeFromKey(int key)228 public static int getTypeFromKey(int key) { 229 return key >>> SETTINGS_TYPE_SHIFT; 230 } 231 getUserIdFromKey(int key)232 public static int getUserIdFromKey(int key) { 233 return key & ~SETTINGS_TYPE_MASK; 234 } 235 settingTypeToString(int type)236 public static String settingTypeToString(int type) { 237 switch (type) { 238 case SETTINGS_TYPE_CONFIG: { 239 return "SETTINGS_CONFIG"; 240 } 241 case SETTINGS_TYPE_GLOBAL: { 242 return "SETTINGS_GLOBAL"; 243 } 244 case SETTINGS_TYPE_SECURE: { 245 return "SETTINGS_SECURE"; 246 } 247 case SETTINGS_TYPE_SYSTEM: { 248 return "SETTINGS_SYSTEM"; 249 } 250 case SETTINGS_TYPE_SSAID: { 251 return "SETTINGS_SSAID"; 252 } 253 default: { 254 return "UNKNOWN"; 255 } 256 } 257 } 258 isConfigSettingsKey(int key)259 public static boolean isConfigSettingsKey(int key) { 260 return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG; 261 } 262 isGlobalSettingsKey(int key)263 public static boolean isGlobalSettingsKey(int key) { 264 return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL; 265 } 266 isSystemSettingsKey(int key)267 public static boolean isSystemSettingsKey(int key) { 268 return getTypeFromKey(key) == SETTINGS_TYPE_SYSTEM; 269 } 270 isSecureSettingsKey(int key)271 public static boolean isSecureSettingsKey(int key) { 272 return getTypeFromKey(key) == SETTINGS_TYPE_SECURE; 273 } 274 isSsaidSettingsKey(int key)275 public static boolean isSsaidSettingsKey(int key) { 276 return getTypeFromKey(key) == SETTINGS_TYPE_SSAID; 277 } 278 keyToString(int key)279 public static String keyToString(int key) { 280 return "Key[user=" + getUserIdFromKey(key) + ";type=" 281 + settingTypeToString(getTypeFromKey(key)) + "]"; 282 } 283 SettingsState(Context context, Object lock, File file, int key, int maxBytesPerAppPackage, Looper looper)284 public SettingsState(Context context, Object lock, File file, int key, 285 int maxBytesPerAppPackage, Looper looper) { 286 // It is important that we use the same lock as the settings provider 287 // to ensure multiple mutations on this state are atomically persisted 288 // as the async persistence should be blocked while we make changes. 289 mContext = context; 290 mLock = lock; 291 mStatePersistFile = file; 292 mStatePersistTag = "settings-" + getTypeFromKey(key) + "-" + getUserIdFromKey(key); 293 mKey = key; 294 mHandler = new MyHandler(looper); 295 if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) { 296 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 297 mPackageToMemoryUsage = new ArrayMap<>(); 298 } else { 299 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 300 mPackageToMemoryUsage = null; 301 } 302 303 mHistoricalOperations = Build.IS_DEBUGGABLE 304 ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; 305 306 synchronized (mLock) { 307 readStateSyncLocked(); 308 } 309 } 310 311 // The settings provider must hold its lock when calling here. 312 @GuardedBy("mLock") getVersionLocked()313 public int getVersionLocked() { 314 return mVersion; 315 } 316 getNullSetting()317 public Setting getNullSetting() { 318 return mNullSetting; 319 } 320 321 // The settings provider must hold its lock when calling here. 322 @GuardedBy("mLock") setVersionLocked(int version)323 public void setVersionLocked(int version) { 324 if (version == mVersion) { 325 return; 326 } 327 mVersion = version; 328 329 scheduleWriteIfNeededLocked(); 330 } 331 332 // The settings provider must hold its lock when calling here. 333 @GuardedBy("mLock") removeSettingsForPackageLocked(String packageName)334 public void removeSettingsForPackageLocked(String packageName) { 335 boolean removedSomething = false; 336 337 final int settingCount = mSettings.size(); 338 for (int i = settingCount - 1; i >= 0; i--) { 339 String name = mSettings.keyAt(i); 340 // Settings defined by us are never dropped. 341 if (Settings.System.PUBLIC_SETTINGS.contains(name) 342 || Settings.System.PRIVATE_SETTINGS.contains(name)) { 343 continue; 344 } 345 Setting setting = mSettings.valueAt(i); 346 if (packageName.equals(setting.packageName)) { 347 mSettings.removeAt(i); 348 removedSomething = true; 349 } 350 } 351 352 if (removedSomething) { 353 scheduleWriteIfNeededLocked(); 354 } 355 } 356 357 // The settings provider must hold its lock when calling here. 358 @GuardedBy("mLock") getSettingNamesLocked()359 public List<String> getSettingNamesLocked() { 360 ArrayList<String> names = new ArrayList<>(); 361 final int settingsCount = mSettings.size(); 362 for (int i = 0; i < settingsCount; i++) { 363 String name = mSettings.keyAt(i); 364 names.add(name); 365 } 366 return names; 367 } 368 369 // The settings provider must hold its lock when calling here. getSettingLocked(String name)370 public Setting getSettingLocked(String name) { 371 if (TextUtils.isEmpty(name)) { 372 return mNullSetting; 373 } 374 Setting setting = mSettings.get(name); 375 if (setting != null) { 376 return new Setting(setting); 377 } 378 return mNullSetting; 379 } 380 381 // The settings provider must hold its lock when calling here. updateSettingLocked(String name, String value, String tag, boolean makeValue, String packageName)382 public boolean updateSettingLocked(String name, String value, String tag, 383 boolean makeValue, String packageName) { 384 if (!hasSettingLocked(name)) { 385 return false; 386 } 387 388 return insertSettingLocked(name, value, tag, makeValue, packageName); 389 } 390 391 // The settings provider must hold its lock when calling here. 392 @GuardedBy("mLock") resetSettingDefaultValueLocked(String name)393 public void resetSettingDefaultValueLocked(String name) { 394 Setting oldSetting = getSettingLocked(name); 395 if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) { 396 String oldValue = oldSetting.getValue(); 397 String oldDefaultValue = oldSetting.getDefaultValue(); 398 Setting newSetting = new Setting(name, oldSetting.getValue(), null, 399 oldSetting.getPackageName(), oldSetting.getTag(), false, 400 oldSetting.getId()); 401 int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0, 402 oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); 403 checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); 404 mSettings.put(name, newSetting); 405 updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); 406 scheduleWriteIfNeededLocked(); 407 } 408 } 409 410 // The settings provider must hold its lock when calling here. insertSettingOverrideableByRestoreLocked(String name, String value, String tag, boolean makeDefault, String packageName)411 public boolean insertSettingOverrideableByRestoreLocked(String name, String value, String tag, 412 boolean makeDefault, String packageName) { 413 return insertSettingLocked(name, value, tag, makeDefault, false, packageName, 414 /* overrideableByRestore */ true); 415 } 416 417 // The settings provider must hold its lock when calling here. 418 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, String packageName)419 public boolean insertSettingLocked(String name, String value, String tag, 420 boolean makeDefault, String packageName) { 421 return insertSettingLocked(name, value, tag, makeDefault, false, packageName, 422 /* overrideableByRestore */ false); 423 } 424 425 // The settings provider must hold its lock when calling here. 426 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName, boolean overrideableByRestore)427 public boolean insertSettingLocked(String name, String value, String tag, 428 boolean makeDefault, boolean forceNonSystemPackage, String packageName, 429 boolean overrideableByRestore) { 430 if (TextUtils.isEmpty(name)) { 431 return false; 432 } 433 434 final boolean isNameTooLong = name.length() > SettingsState.MAX_LENGTH_PER_STRING; 435 final boolean isValueTooLong = 436 value != null && value.length() > SettingsState.MAX_LENGTH_PER_STRING; 437 if (isNameTooLong || isValueTooLong) { 438 // only print the first few bytes of the name in case it is long 439 final String errorMessage = "The " + (isNameTooLong ? "name" : "value") 440 + " of your setting [" 441 + (name.length() > 20 ? (name.substring(0, 20) + "...") : name) 442 + "] is too long. The max length allowed for the string is " 443 + MAX_LENGTH_PER_STRING + "."; 444 throw new IllegalArgumentException(errorMessage); 445 } 446 447 Setting oldState = mSettings.get(name); 448 String oldValue = (oldState != null) ? oldState.value : null; 449 String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; 450 String newDefaultValue = makeDefault ? value : oldDefaultValue; 451 452 int newSize = getNewMemoryUsagePerPackageLocked(packageName, 453 oldValue == null ? name.length() : 0 /* deltaKeySize */, 454 oldValue, value, oldDefaultValue, newDefaultValue); 455 checkNewMemoryUsagePerPackageLocked(packageName, newSize); 456 457 Setting newState; 458 459 if (oldState != null) { 460 if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage, 461 overrideableByRestore)) { 462 return false; 463 } 464 newState = oldState; 465 } else { 466 newState = new Setting(name, value, makeDefault, packageName, tag, 467 forceNonSystemPackage); 468 mSettings.put(name, newState); 469 } 470 471 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, value, newState.value, 472 oldValue, tag, makeDefault, getUserIdFromKey(mKey), 473 FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED); 474 475 addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); 476 477 updateMemoryUsagePerPackageLocked(packageName, newSize); 478 479 scheduleWriteIfNeededLocked(); 480 481 return true; 482 } 483 484 @GuardedBy("mLock") isNewConfigBannedLocked(String prefix, Map<String, String> keyValues)485 public boolean isNewConfigBannedLocked(String prefix, Map<String, String> keyValues) { 486 // Replaces old style "null" String values with actual null's. This is done to simulate 487 // what will happen to String "null" values when they are written to Settings. This needs to 488 // be done here make sure that config hash computed during is banned check matches the 489 // one computed during banning when values are already stored. 490 keyValues = removeNullValueOldStyle(keyValues); 491 String bannedHash = mNamespaceBannedHashes.get(prefix); 492 if (bannedHash == null) { 493 return false; 494 } 495 return bannedHash.equals(hashCode(keyValues)); 496 } 497 498 @GuardedBy("mLock") unbanAllConfigIfBannedConfigUpdatedLocked(String prefix)499 public void unbanAllConfigIfBannedConfigUpdatedLocked(String prefix) { 500 // If the prefix updated is a banned namespace, clear mNamespaceBannedHashes 501 // to unban all unbanned namespaces. 502 if (mNamespaceBannedHashes.get(prefix) != null) { 503 mNamespaceBannedHashes.clear(); 504 scheduleWriteIfNeededLocked(); 505 } 506 } 507 508 @GuardedBy("mLock") banConfigurationLocked(String prefix, Map<String, String> keyValues)509 public void banConfigurationLocked(String prefix, Map<String, String> keyValues) { 510 if (prefix == null || keyValues.isEmpty()) { 511 return; 512 } 513 // The write is intentionally not scheduled here, banned hashes should and will be written 514 // when the related setting changes are written 515 mNamespaceBannedHashes.put(prefix, hashCode(keyValues)); 516 } 517 518 @GuardedBy("mLock") getAllConfigPrefixesLocked()519 public Set<String> getAllConfigPrefixesLocked() { 520 Set<String> prefixSet = new HashSet<>(); 521 final int settingsCount = mSettings.size(); 522 for (int i = 0; i < settingsCount; i++) { 523 String name = mSettings.keyAt(i); 524 prefixSet.add(name.split("/")[0] + "/"); 525 } 526 return prefixSet; 527 } 528 529 // The settings provider must hold its lock when calling here. 530 // Returns the list of keys which changed (added, updated, or deleted). 531 @GuardedBy("mLock") setSettingsLocked(String prefix, Map<String, String> keyValues, String packageName)532 public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues, 533 String packageName) { 534 List<String> changedKeys = new ArrayList<>(); 535 final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator(); 536 // Delete old keys with the prefix that are not part of the new set. 537 while (iterator.hasNext()) { 538 Map.Entry<String, Setting> entry = iterator.next(); 539 final String key = entry.getKey(); 540 final Setting oldState = entry.getValue(); 541 if (key != null && key.startsWith(prefix) && !keyValues.containsKey(key)) { 542 iterator.remove(); 543 544 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, 545 /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, 546 getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); 547 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); 548 changedKeys.add(key); // key was removed 549 } 550 } 551 552 // Update/add new keys 553 for (String key : keyValues.keySet()) { 554 String value = keyValues.get(key); 555 String oldValue = null; 556 Setting state = mSettings.get(key); 557 if (state == null) { 558 state = new Setting(key, value, false, packageName, null); 559 mSettings.put(key, state); 560 changedKeys.add(key); // key was added 561 } else if (state.value != value) { 562 oldValue = state.value; 563 state.update(value, false, packageName, null, true, 564 /* overrideableByRestore */ false); 565 changedKeys.add(key); // key was updated 566 } else { 567 // this key/value already exists, no change and no logging necessary 568 continue; 569 } 570 571 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, value, state.value, 572 oldValue, /* tag */ null, /* make default */ false, 573 getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED); 574 addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, state); 575 } 576 577 if (!changedKeys.isEmpty()) { 578 scheduleWriteIfNeededLocked(); 579 } 580 581 return changedKeys; 582 } 583 584 // The settings provider must hold its lock when calling here. persistSyncLocked()585 public void persistSyncLocked() { 586 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 587 doWriteState(); 588 } 589 590 // The settings provider must hold its lock when calling here. 591 @GuardedBy("mLock") deleteSettingLocked(String name)592 public boolean deleteSettingLocked(String name) { 593 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 594 return false; 595 } 596 597 Setting oldState = mSettings.remove(name); 598 if (oldState == null) { 599 return false; 600 } 601 int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, 602 -name.length() /* deltaKeySize */, 603 oldState.value, null, oldState.defaultValue, null); 604 605 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", 606 /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), 607 FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); 608 609 updateMemoryUsagePerPackageLocked(oldState.packageName, newSize); 610 611 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); 612 613 scheduleWriteIfNeededLocked(); 614 615 return true; 616 } 617 618 // The settings provider must hold its lock when calling here. 619 @GuardedBy("mLock") resetSettingLocked(String name)620 public boolean resetSettingLocked(String name) { 621 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 622 return false; 623 } 624 625 Setting setting = mSettings.get(name); 626 if (setting == null) { 627 return false; 628 } 629 630 Setting oldSetting = new Setting(setting); 631 String oldValue = setting.getValue(); 632 String oldDefaultValue = setting.getDefaultValue(); 633 634 int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue, 635 oldDefaultValue, oldDefaultValue, oldDefaultValue); 636 checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); 637 638 if (!setting.reset()) { 639 return false; 640 } 641 642 updateMemoryUsagePerPackageLocked(setting.packageName, newSize); 643 644 addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); 645 646 scheduleWriteIfNeededLocked(); 647 648 return true; 649 } 650 651 // The settings provider must hold its lock when calling here. 652 @GuardedBy("mLock") destroyLocked(Runnable callback)653 public void destroyLocked(Runnable callback) { 654 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 655 if (callback != null) { 656 if (mDirty) { 657 // Do it without a delay. 658 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS, 659 callback).sendToTarget(); 660 return; 661 } 662 callback.run(); 663 } 664 } 665 666 @GuardedBy("mLock") addHistoricalOperationLocked(String type, Setting setting)667 private void addHistoricalOperationLocked(String type, Setting setting) { 668 if (mHistoricalOperations == null) { 669 return; 670 } 671 HistoricalOperation operation = new HistoricalOperation( 672 System.currentTimeMillis(), type, 673 setting != null ? new Setting(setting) : null); 674 if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) { 675 mHistoricalOperations.add(operation); 676 } else { 677 mHistoricalOperations.set(mNextHistoricalOpIdx, operation); 678 } 679 mNextHistoricalOpIdx++; 680 if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) { 681 mNextHistoricalOpIdx = 0; 682 } 683 } 684 685 /** 686 * Dump historical operations as a proto buf. 687 * 688 * @param proto The proto buf stream to dump to 689 * @param fieldId The repeated field ID to use to save an operation to. 690 */ dumpHistoricalOperations(@onNull ProtoOutputStream proto, long fieldId)691 void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) { 692 synchronized (mLock) { 693 if (mHistoricalOperations == null) { 694 return; 695 } 696 697 final int operationCount = mHistoricalOperations.size(); 698 for (int i = 0; i < operationCount; i++) { 699 int index = mNextHistoricalOpIdx - 1 - i; 700 if (index < 0) { 701 index = operationCount + index; 702 } 703 HistoricalOperation operation = mHistoricalOperations.get(index); 704 705 final long token = proto.start(fieldId); 706 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp); 707 proto.write(SettingsOperationProto.OPERATION, operation.mOperation); 708 if (operation.mSetting != null) { 709 // Only add the name of the setting, since we don't know the historical package 710 // and values for it so they would be misleading to add here (all we could 711 // add is what the current data is). 712 proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName()); 713 } 714 proto.end(token); 715 } 716 } 717 } 718 dumpHistoricalOperations(PrintWriter pw)719 public void dumpHistoricalOperations(PrintWriter pw) { 720 synchronized (mLock) { 721 if (mHistoricalOperations == null) { 722 return; 723 } 724 pw.println("Historical operations"); 725 final int operationCount = mHistoricalOperations.size(); 726 for (int i = 0; i < operationCount; i++) { 727 int index = mNextHistoricalOpIdx - 1 - i; 728 if (index < 0) { 729 index = operationCount + index; 730 } 731 HistoricalOperation operation = mHistoricalOperations.get(index); 732 pw.print(TimeUtils.formatForLogging(operation.mTimestamp)); 733 pw.print(" "); 734 pw.print(operation.mOperation); 735 if (operation.mSetting != null) { 736 pw.print(" "); 737 // Only print the name of the setting, since we don't know the 738 // historical package and values for it so they would be misleading 739 // to print here (all we could print is what the current data is). 740 pw.print(operation.mSetting.getName()); 741 } 742 pw.println(); 743 } 744 pw.println(); 745 pw.println(); 746 } 747 } 748 749 @GuardedBy("mLock") isExemptFromMemoryUsageCap(String packageName)750 private boolean isExemptFromMemoryUsageCap(String packageName) { 751 return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED 752 || SYSTEM_PACKAGE_NAME.equals(packageName); 753 } 754 755 @GuardedBy("mLock") checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)756 private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize) 757 throws IllegalStateException { 758 if (isExemptFromMemoryUsageCap(packageName)) { 759 return; 760 } 761 if (newSize > mMaxBytesPerAppPackage) { 762 throw new IllegalStateException("You are adding too many system settings. " 763 + "You should stop using system settings for app specific data" 764 + " package: " + packageName); 765 } 766 } 767 768 @GuardedBy("mLock") getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeyLength, String oldValue, String newValue, String oldDefaultValue, String newDefaultValue)769 private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeyLength, 770 String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) { 771 if (isExemptFromMemoryUsageCap(packageName)) { 772 return 0; 773 } 774 final int currentSize = mPackageToMemoryUsage.getOrDefault(packageName, 0); 775 final int oldValueLength = (oldValue != null) ? oldValue.length() : 0; 776 final int newValueLength = (newValue != null) ? newValue.length() : 0; 777 final int oldDefaultValueLength = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; 778 final int newDefaultValueLength = (newDefaultValue != null) ? newDefaultValue.length() : 0; 779 final int deltaSize = (deltaKeyLength + newValueLength + newDefaultValueLength 780 - oldValueLength - oldDefaultValueLength) * Character.BYTES; 781 return Math.max(currentSize + deltaSize, 0); 782 } 783 784 @GuardedBy("mLock") updateMemoryUsagePerPackageLocked(String packageName, int newSize)785 private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) { 786 if (isExemptFromMemoryUsageCap(packageName)) { 787 return; 788 } 789 if (DEBUG) { 790 Slog.i(LOG_TAG, "Settings for package: " + packageName 791 + " size: " + newSize + " bytes."); 792 } 793 mPackageToMemoryUsage.put(packageName, newSize); 794 } 795 hasSetting(String name)796 public boolean hasSetting(String name) { 797 synchronized (mLock) { 798 return hasSettingLocked(name); 799 } 800 } 801 802 @GuardedBy("mLock") hasSettingLocked(String name)803 private boolean hasSettingLocked(String name) { 804 return mSettings.indexOfKey(name) >= 0; 805 } 806 807 @GuardedBy("mLock") scheduleWriteIfNeededLocked()808 private void scheduleWriteIfNeededLocked() { 809 // If dirty then we have a write already scheduled. 810 if (!mDirty) { 811 mDirty = true; 812 writeStateAsyncLocked(); 813 } 814 } 815 816 @GuardedBy("mLock") writeStateAsyncLocked()817 private void writeStateAsyncLocked() { 818 final long currentTimeMillis = SystemClock.uptimeMillis(); 819 820 if (mWriteScheduled) { 821 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 822 823 // If enough time passed, write without holding off anymore. 824 final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis 825 - mLastNotWrittenMutationTimeMillis; 826 if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) { 827 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget(); 828 return; 829 } 830 831 // Hold off a bit more as settings are frequently changing. 832 final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis 833 + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0); 834 final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis); 835 836 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 837 mHandler.sendMessageDelayed(message, writeDelayMillis); 838 } else { 839 mLastNotWrittenMutationTimeMillis = currentTimeMillis; 840 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 841 mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS); 842 mWriteScheduled = true; 843 } 844 } 845 doWriteState()846 private void doWriteState() { 847 boolean wroteState = false; 848 String settingFailedToBePersisted = null; 849 final int version; 850 final ArrayMap<String, Setting> settings; 851 final ArrayMap<String, String> namespaceBannedHashes; 852 853 synchronized (mLock) { 854 version = mVersion; 855 settings = new ArrayMap<>(mSettings); 856 namespaceBannedHashes = new ArrayMap<>(mNamespaceBannedHashes); 857 mDirty = false; 858 mWriteScheduled = false; 859 } 860 861 synchronized (mWriteLock) { 862 if (DEBUG_PERSISTENCE) { 863 Slog.i(LOG_TAG, "[PERSIST START]"); 864 } 865 866 AtomicFile destination = new AtomicFile(mStatePersistFile, mStatePersistTag); 867 FileOutputStream out = null; 868 try { 869 out = destination.startWrite(); 870 871 TypedXmlSerializer serializer = Xml.resolveSerializer(out); 872 serializer.startDocument(null, true); 873 serializer.startTag(null, TAG_SETTINGS); 874 serializer.attributeInt(null, ATTR_VERSION, version); 875 876 final int settingCount = settings.size(); 877 for (int i = 0; i < settingCount; i++) { 878 Setting setting = settings.valueAt(i); 879 if (setting.isTransient()) { 880 if (DEBUG_PERSISTENCE) { 881 Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName()); 882 } 883 continue; 884 } 885 886 try { 887 if (writeSingleSetting(mVersion, serializer, setting.getId(), 888 setting.getName(), 889 setting.getValue(), setting.getDefaultValue(), 890 setting.getPackageName(), 891 setting.getTag(), setting.isDefaultFromSystem(), 892 setting.isValuePreservedInRestore())) { 893 if (DEBUG_PERSISTENCE) { 894 Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" 895 + setting.getValue()); 896 } 897 } 898 } catch (IOException ex) { 899 Slog.e(LOG_TAG, "[ABORT PERSISTING]" + setting.getName() 900 + " due to error writing to disk", ex); 901 // A setting failed to be written. Abort the serialization to avoid leaving 902 // a partially serialized setting on disk, which can cause parsing errors. 903 // Note down the problematic setting, so that we can delete it before trying 904 // again to persist the rest of the settings. 905 settingFailedToBePersisted = setting.getName(); 906 throw ex; 907 } 908 } 909 serializer.endTag(null, TAG_SETTINGS); 910 911 serializer.startTag(null, TAG_NAMESPACE_HASHES); 912 for (int i = 0; i < namespaceBannedHashes.size(); i++) { 913 String namespace = namespaceBannedHashes.keyAt(i); 914 String bannedHash = namespaceBannedHashes.get(namespace); 915 if (writeSingleNamespaceHash(serializer, namespace, bannedHash)) { 916 if (DEBUG_PERSISTENCE) { 917 Slog.i(LOG_TAG, "[PERSISTED] namespace=" + namespace 918 + ", bannedHash=" + bannedHash); 919 } 920 } 921 } 922 serializer.endTag(null, TAG_NAMESPACE_HASHES); 923 serializer.endDocument(); 924 destination.finishWrite(out); 925 926 wroteState = true; 927 928 if (DEBUG_PERSISTENCE) { 929 Slog.i(LOG_TAG, "[PERSIST END]"); 930 } 931 } catch (Throwable t) { 932 Slog.wtf(LOG_TAG, "Failed to write settings, restoring old file", t); 933 if (t instanceof IOException) { 934 if (t.getMessage().contains("Couldn't create directory")) { 935 if (DEBUG) { 936 // we failed to create a directory, so log the permissions and existence 937 // state for the settings file and directory 938 logSettingsDirectoryInformation(destination.getBaseFile()); 939 } 940 // attempt to create the directory with Files.createDirectories, which 941 // throws more informative errors than File.mkdirs. 942 Path parentPath = destination.getBaseFile().getParentFile().toPath(); 943 try { 944 Files.createDirectories(parentPath); 945 if (DEBUG) { 946 Slog.i(LOG_TAG, "Successfully created " + parentPath); 947 } 948 } catch (Throwable t2) { 949 Slog.e(LOG_TAG, "Failed to write " + parentPath 950 + " with Files.writeDirectories", t2); 951 } 952 } 953 } 954 destination.failWrite(out); 955 } finally { 956 IoUtils.closeQuietly(out); 957 } 958 } 959 960 if (!wroteState) { 961 if (settingFailedToBePersisted != null) { 962 synchronized (mLock) { 963 // Delete the problematic setting. This will schedule a write as well. 964 deleteSettingLocked(settingFailedToBePersisted); 965 } 966 } 967 } else { 968 // success 969 synchronized (mLock) { 970 addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null); 971 } 972 } 973 } 974 logSettingsDirectoryInformation(File settingsFile)975 private static void logSettingsDirectoryInformation(File settingsFile) { 976 File parent = settingsFile.getParentFile(); 977 Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile 978 + " with stacktrace ", new Exception()); 979 File ancestorDir = parent; 980 while (ancestorDir != null) { 981 if (!ancestorDir.exists()) { 982 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 983 + " does not exist"); 984 ancestorDir = ancestorDir.getParentFile(); 985 } else { 986 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 987 + " exists"); 988 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 989 + " permissions: r: " + ancestorDir.canRead() + " w: " 990 + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute()); 991 File ancestorParent = ancestorDir.getParentFile(); 992 if (ancestorParent != null) { 993 Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent 994 + " permissions: r: " + ancestorParent.canRead() + " w: " 995 + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute()); 996 } 997 break; 998 } 999 } 1000 } 1001 writeSingleSetting(int version, TypedXmlSerializer serializer, String id, String name, String value, String defaultValue, String packageName, String tag, boolean defaultSysSet, boolean isValuePreservedInRestore)1002 static boolean writeSingleSetting(int version, TypedXmlSerializer serializer, String id, 1003 String name, String value, String defaultValue, String packageName, 1004 String tag, boolean defaultSysSet, boolean isValuePreservedInRestore) 1005 throws IOException { 1006 if (id == null || isBinary(id) || name == null || isBinary(name) 1007 || packageName == null || isBinary(packageName)) { 1008 if (DEBUG_PERSISTENCE) { 1009 Slog.w(LOG_TAG, "Invalid arguments for writeSingleSetting: version=" + version 1010 + ", id=" + id + ", name=" + name + ", value=" + value 1011 + ", defaultValue=" + defaultValue + ", packageName=" + packageName 1012 + ", tag=" + tag + ", defaultSysSet=" + defaultSysSet 1013 + ", isValuePreservedInRestore=" + isValuePreservedInRestore); 1014 } 1015 return false; 1016 } 1017 serializer.startTag(null, TAG_SETTING); 1018 serializer.attribute(null, ATTR_ID, id); 1019 serializer.attribute(null, ATTR_NAME, name); 1020 setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64, 1021 version, serializer, value); 1022 serializer.attribute(null, ATTR_PACKAGE, packageName); 1023 if (defaultValue != null) { 1024 setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64, 1025 version, serializer, defaultValue); 1026 serializer.attributeBoolean(null, ATTR_DEFAULT_SYS_SET, defaultSysSet); 1027 setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64, 1028 version, serializer, tag); 1029 } 1030 if (isValuePreservedInRestore) { 1031 serializer.attributeBoolean(null, ATTR_PRESERVE_IN_RESTORE, true); 1032 } 1033 serializer.endTag(null, TAG_SETTING); 1034 return true; 1035 } 1036 setValueAttribute(String attr, String attrBase64, int version, TypedXmlSerializer serializer, String value)1037 static void setValueAttribute(String attr, String attrBase64, int version, 1038 TypedXmlSerializer serializer, String value) throws IOException { 1039 if (version >= SETTINGS_VERSION_NEW_ENCODING) { 1040 if (value == null) { 1041 // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64. 1042 } else if (isBinary(value)) { 1043 serializer.attribute(null, attrBase64, base64Encode(value)); 1044 } else { 1045 serializer.attribute(null, attr, value); 1046 } 1047 } else { 1048 // Old encoding. 1049 if (value == null) { 1050 serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE); 1051 } else { 1052 serializer.attribute(null, attr, value); 1053 } 1054 } 1055 } 1056 writeSingleNamespaceHash(TypedXmlSerializer serializer, String namespace, String bannedHashCode)1057 private static boolean writeSingleNamespaceHash(TypedXmlSerializer serializer, String namespace, 1058 String bannedHashCode) throws IOException { 1059 if (namespace == null || bannedHashCode == null) { 1060 if (DEBUG_PERSISTENCE) { 1061 Slog.w(LOG_TAG, "Invalid arguments for writeSingleNamespaceHash: namespace=" 1062 + namespace + ", bannedHashCode=" + bannedHashCode); 1063 } 1064 return false; 1065 } 1066 serializer.startTag(null, TAG_NAMESPACE_HASH); 1067 serializer.attribute(null, ATTR_NAMESPACE, namespace); 1068 serializer.attribute(null, ATTR_BANNED_HASH, bannedHashCode); 1069 serializer.endTag(null, TAG_NAMESPACE_HASH); 1070 return true; 1071 } 1072 hashCode(Map<String, String> keyValues)1073 private static String hashCode(Map<String, String> keyValues) { 1074 return Integer.toString(keyValues.hashCode()); 1075 } 1076 getValueAttribute(TypedXmlPullParser parser, String attr, String base64Attr)1077 private String getValueAttribute(TypedXmlPullParser parser, String attr, String base64Attr) { 1078 if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) { 1079 final String value = parser.getAttributeValue(null, attr); 1080 if (value != null) { 1081 return value; 1082 } 1083 final String base64 = parser.getAttributeValue(null, base64Attr); 1084 if (base64 != null) { 1085 return base64Decode(base64); 1086 } 1087 // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64. 1088 return null; 1089 } else { 1090 // Old encoding. 1091 final String stored = parser.getAttributeValue(null, attr); 1092 if (NULL_VALUE_OLD_STYLE.equals(stored)) { 1093 return null; 1094 } else { 1095 return stored; 1096 } 1097 } 1098 } 1099 1100 @GuardedBy("mLock") readStateSyncLocked()1101 private void readStateSyncLocked() throws IllegalStateException { 1102 FileInputStream in; 1103 AtomicFile file = new AtomicFile(mStatePersistFile); 1104 try { 1105 in = file.openRead(); 1106 } catch (FileNotFoundException fnfe) { 1107 Slog.w(LOG_TAG, "No settings state " + mStatePersistFile); 1108 if (DEBUG) { 1109 logSettingsDirectoryInformation(mStatePersistFile); 1110 } 1111 addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null); 1112 return; 1113 } 1114 if (parseStateFromXmlStreamLocked(in)) { 1115 return; 1116 } 1117 1118 // Settings file exists but is corrupted. Retry with the fallback file 1119 final File statePersistFallbackFile = new File( 1120 mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX); 1121 Slog.w(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile 1122 + ", retrying with fallback file: " + statePersistFallbackFile); 1123 try { 1124 in = new AtomicFile(statePersistFallbackFile).openRead(); 1125 } catch (FileNotFoundException fnfe) { 1126 final String message = "No fallback file found for: " + mStatePersistFile; 1127 Slog.wtf(LOG_TAG, message); 1128 if (!isConfigSettingsKey(mKey)) { 1129 // Allow partially deserialized config settings because they can be updated later 1130 throw new IllegalStateException(message); 1131 } 1132 } 1133 if (parseStateFromXmlStreamLocked(in)) { 1134 // Parsed state from fallback file. Restore original file with fallback file 1135 try { 1136 FileUtils.copy(statePersistFallbackFile, mStatePersistFile); 1137 } catch (IOException ignored) { 1138 // Failed to copy, but it's okay because we already parsed states from fallback file 1139 } 1140 } else { 1141 final String message = "Failed parsing settings file: " + mStatePersistFile; 1142 Slog.wtf(LOG_TAG, message); 1143 if (!isConfigSettingsKey(mKey)) { 1144 // Allow partially deserialized config settings because they can be updated later 1145 throw new IllegalStateException(message); 1146 } 1147 } 1148 } 1149 1150 @GuardedBy("mLock") parseStateFromXmlStreamLocked(FileInputStream in)1151 private boolean parseStateFromXmlStreamLocked(FileInputStream in) { 1152 try { 1153 TypedXmlPullParser parser = Xml.resolvePullParser(in); 1154 parseStateLocked(parser); 1155 return true; 1156 } catch (XmlPullParserException | IOException e) { 1157 Slog.e(LOG_TAG, "parse settings xml failed", e); 1158 return false; 1159 } finally { 1160 IoUtils.closeQuietly(in); 1161 } 1162 } 1163 1164 /** 1165 * Uses AtomicFile to check if the file or its backup exists. 1166 * 1167 * @param file The file to check for existence 1168 * @return whether the original or backup exist 1169 */ stateFileExists(File file)1170 public static boolean stateFileExists(File file) { 1171 AtomicFile stateFile = new AtomicFile(file); 1172 return stateFile.exists(); 1173 } 1174 parseStateLocked(TypedXmlPullParser parser)1175 private void parseStateLocked(TypedXmlPullParser parser) 1176 throws IOException, XmlPullParserException { 1177 final int outerDepth = parser.getDepth(); 1178 int type; 1179 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1180 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1181 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1182 continue; 1183 } 1184 1185 String tagName = parser.getName(); 1186 if (tagName.equals(TAG_SETTINGS)) { 1187 parseSettingsLocked(parser); 1188 } else if (tagName.equals(TAG_NAMESPACE_HASHES)) { 1189 parseNamespaceHash(parser); 1190 } 1191 } 1192 } 1193 1194 @GuardedBy("mLock") parseSettingsLocked(TypedXmlPullParser parser)1195 private void parseSettingsLocked(TypedXmlPullParser parser) 1196 throws IOException, XmlPullParserException { 1197 1198 mVersion = parser.getAttributeInt(null, ATTR_VERSION); 1199 1200 final int outerDepth = parser.getDepth(); 1201 int type; 1202 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1203 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1204 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1205 continue; 1206 } 1207 1208 String tagName = parser.getName(); 1209 if (tagName.equals(TAG_SETTING)) { 1210 String id = parser.getAttributeValue(null, ATTR_ID); 1211 String name = parser.getAttributeValue(null, ATTR_NAME); 1212 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64); 1213 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 1214 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE, 1215 ATTR_DEFAULT_VALUE_BASE64); 1216 boolean isPreservedInRestore = parser.getAttributeBoolean(null, 1217 ATTR_PRESERVE_IN_RESTORE, false); 1218 String tag = null; 1219 boolean fromSystem = false; 1220 if (defaultValue != null) { 1221 fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false); 1222 tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); 1223 } 1224 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, 1225 fromSystem, id, isPreservedInRestore)); 1226 1227 if (DEBUG_PERSISTENCE) { 1228 Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value); 1229 } 1230 } 1231 } 1232 } 1233 1234 @GuardedBy("mLock") parseNamespaceHash(TypedXmlPullParser parser)1235 private void parseNamespaceHash(TypedXmlPullParser parser) 1236 throws IOException, XmlPullParserException { 1237 1238 final int outerDepth = parser.getDepth(); 1239 int type; 1240 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1241 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1242 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1243 continue; 1244 } 1245 1246 if (parser.getName().equals(TAG_NAMESPACE_HASH)) { 1247 String namespace = parser.getAttributeValue(null, ATTR_NAMESPACE); 1248 String bannedHashCode = parser.getAttributeValue(null, ATTR_BANNED_HASH); 1249 mNamespaceBannedHashes.put(namespace, bannedHashCode); 1250 } 1251 } 1252 } 1253 removeNullValueOldStyle(Map<String, String> keyValues)1254 private static Map<String, String> removeNullValueOldStyle(Map<String, String> keyValues) { 1255 Iterator<Map.Entry<String, String>> it = keyValues.entrySet().iterator(); 1256 while (it.hasNext()) { 1257 Map.Entry<String, String> keyValueEntry = it.next(); 1258 if (NULL_VALUE_OLD_STYLE.equals(keyValueEntry.getValue())) { 1259 keyValueEntry.setValue(null); 1260 } 1261 } 1262 return keyValues; 1263 } 1264 1265 private final class MyHandler extends Handler { 1266 public static final int MSG_PERSIST_SETTINGS = 1; 1267 MyHandler(Looper looper)1268 public MyHandler(Looper looper) { 1269 super(looper); 1270 } 1271 1272 @Override handleMessage(Message message)1273 public void handleMessage(Message message) { 1274 switch (message.what) { 1275 case MSG_PERSIST_SETTINGS: { 1276 Runnable callback = (Runnable) message.obj; 1277 doWriteState(); 1278 if (callback != null) { 1279 callback.run(); 1280 } 1281 } 1282 break; 1283 } 1284 } 1285 } 1286 1287 private class HistoricalOperation { 1288 final long mTimestamp; 1289 final String mOperation; 1290 final Setting mSetting; 1291 HistoricalOperation(long timestamp, String operation, Setting setting)1292 public HistoricalOperation(long timestamp, 1293 String operation, Setting setting) { 1294 mTimestamp = timestamp; 1295 mOperation = operation; 1296 mSetting = setting; 1297 } 1298 } 1299 1300 class Setting { 1301 private String name; 1302 private String value; 1303 private String defaultValue; 1304 private String packageName; 1305 private String id; 1306 private String tag; 1307 // Whether the default is set by the system 1308 private boolean defaultFromSystem; 1309 // Whether the value of this setting will be preserved when restore happens. 1310 private boolean isValuePreservedInRestore; 1311 Setting(Setting other)1312 public Setting(Setting other) { 1313 name = other.name; 1314 value = other.value; 1315 defaultValue = other.defaultValue; 1316 packageName = other.packageName; 1317 id = other.id; 1318 defaultFromSystem = other.defaultFromSystem; 1319 tag = other.tag; 1320 isValuePreservedInRestore = other.isValuePreservedInRestore; 1321 } 1322 Setting(String name, String value, boolean makeDefault, String packageName, String tag)1323 public Setting(String name, String value, boolean makeDefault, String packageName, 1324 String tag) { 1325 this(name, value, makeDefault, packageName, tag, false); 1326 } 1327 Setting(String name, String value, boolean makeDefault, String packageName, String tag, boolean forceNonSystemPackage)1328 Setting(String name, String value, boolean makeDefault, String packageName, 1329 String tag, boolean forceNonSystemPackage) { 1330 this.name = name; 1331 // overrideableByRestore = true as the first initialization isn't considered a 1332 // modification. 1333 update(value, makeDefault, packageName, tag, forceNonSystemPackage, true); 1334 } 1335 Setting(String name, String value, String defaultValue, String packageName, String tag, boolean fromSystem, String id)1336 public Setting(String name, String value, String defaultValue, 1337 String packageName, String tag, boolean fromSystem, String id) { 1338 this(name, value, defaultValue, packageName, tag, fromSystem, id, 1339 /* isOverrideableByRestore */ false); 1340 } 1341 Setting(String name, String value, String defaultValue, String packageName, String tag, boolean fromSystem, String id, boolean isValuePreservedInRestore)1342 Setting(String name, String value, String defaultValue, 1343 String packageName, String tag, boolean fromSystem, String id, 1344 boolean isValuePreservedInRestore) { 1345 mNextId = Math.max(mNextId, Long.parseLong(id) + 1); 1346 if (NULL_VALUE.equals(value)) { 1347 value = null; 1348 } 1349 init(name, value, tag, defaultValue, packageName, fromSystem, id, 1350 isValuePreservedInRestore); 1351 } 1352 init(String name, String value, String tag, String defaultValue, String packageName, boolean fromSystem, String id, boolean isValuePreservedInRestore)1353 private void init(String name, String value, String tag, String defaultValue, 1354 String packageName, boolean fromSystem, String id, 1355 boolean isValuePreservedInRestore) { 1356 this.name = name; 1357 this.value = value; 1358 this.tag = tag; 1359 this.defaultValue = defaultValue; 1360 this.packageName = packageName; 1361 this.id = id; 1362 this.defaultFromSystem = fromSystem; 1363 this.isValuePreservedInRestore = isValuePreservedInRestore; 1364 } 1365 getName()1366 public String getName() { 1367 return name; 1368 } 1369 getKey()1370 public int getKey() { 1371 return mKey; 1372 } 1373 getValue()1374 public String getValue() { 1375 return value; 1376 } 1377 getTag()1378 public String getTag() { 1379 return tag; 1380 } 1381 getDefaultValue()1382 public String getDefaultValue() { 1383 return defaultValue; 1384 } 1385 getPackageName()1386 public String getPackageName() { 1387 return packageName; 1388 } 1389 isDefaultFromSystem()1390 public boolean isDefaultFromSystem() { 1391 return defaultFromSystem; 1392 } 1393 isValuePreservedInRestore()1394 public boolean isValuePreservedInRestore() { 1395 return isValuePreservedInRestore; 1396 } 1397 getId()1398 public String getId() { 1399 return id; 1400 } 1401 isNull()1402 public boolean isNull() { 1403 return false; 1404 } 1405 1406 /** @return whether the value changed */ reset()1407 public boolean reset() { 1408 // overrideableByRestore = true as resetting to default value isn't considered a 1409 // modification. 1410 return update(this.defaultValue, false, packageName, null, true, true, 1411 /* resetToDefault */ true); 1412 } 1413 isTransient()1414 public boolean isTransient() { 1415 switch (getTypeFromKey(getKey())) { 1416 case SETTINGS_TYPE_GLOBAL: 1417 return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName()); 1418 } 1419 return false; 1420 } 1421 update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage, boolean overrideableByRestore)1422 public boolean update(String value, boolean setDefault, String packageName, String tag, 1423 boolean forceNonSystemPackage, boolean overrideableByRestore) { 1424 return update(value, setDefault, packageName, tag, forceNonSystemPackage, 1425 overrideableByRestore, /* resetToDefault */ false); 1426 } 1427 update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage, boolean overrideableByRestore, boolean resetToDefault)1428 private boolean update(String value, boolean setDefault, String packageName, String tag, 1429 boolean forceNonSystemPackage, boolean overrideableByRestore, 1430 boolean resetToDefault) { 1431 if (NULL_VALUE.equals(value)) { 1432 value = null; 1433 } 1434 final boolean callerSystem = !forceNonSystemPackage && 1435 !isNull() && (isCalledFromSystem(packageName) 1436 || isSystemPackage(mContext, packageName)); 1437 // Settings set by the system are always defaults. 1438 if (callerSystem) { 1439 setDefault = true; 1440 } 1441 1442 String defaultValue = this.defaultValue; 1443 boolean defaultFromSystem = this.defaultFromSystem; 1444 if (setDefault) { 1445 if (!Objects.equals(value, this.defaultValue) 1446 && (!defaultFromSystem || callerSystem)) { 1447 defaultValue = value; 1448 // Default null means no default, so the tag is irrelevant 1449 // since it is used to reset a settings subset their defaults. 1450 // Also it is irrelevant if the system set the canonical default. 1451 if (defaultValue == null) { 1452 tag = null; 1453 defaultFromSystem = false; 1454 } 1455 } 1456 if (!defaultFromSystem && value != null) { 1457 if (callerSystem) { 1458 defaultFromSystem = true; 1459 } 1460 } 1461 } 1462 1463 // isValuePreservedInRestore shouldn't change back to false if it has been set to true. 1464 boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault, 1465 packageName, value); 1466 1467 // Is something gonna change? 1468 if (Objects.equals(value, this.value) 1469 && Objects.equals(defaultValue, this.defaultValue) 1470 && Objects.equals(packageName, this.packageName) 1471 && Objects.equals(tag, this.tag) 1472 && defaultFromSystem == this.defaultFromSystem 1473 && isPreserved == this.isValuePreservedInRestore) { 1474 return false; 1475 } 1476 1477 init(name, value, tag, defaultValue, packageName, defaultFromSystem, 1478 String.valueOf(mNextId++), isPreserved); 1479 1480 return true; 1481 } 1482 toString()1483 public String toString() { 1484 return "Setting{name=" + name + " value=" + value 1485 + (defaultValue != null ? " default=" + defaultValue : "") 1486 + " packageName=" + packageName + " tag=" + tag 1487 + " defaultFromSystem=" + defaultFromSystem + "}"; 1488 } 1489 shouldPreserveSetting(boolean overrideableByRestore, boolean resetToDefault, String packageName, String value)1490 private boolean shouldPreserveSetting(boolean overrideableByRestore, 1491 boolean resetToDefault, String packageName, String value) { 1492 if (resetToDefault) { 1493 // By default settings are not marked as preserved. 1494 return false; 1495 } 1496 if (value != null && value.equals(this.value) 1497 && SYSTEM_PACKAGE_NAME.equals(packageName)) { 1498 // Do not mark preserved if it's the system reinitializing to the same value. 1499 return false; 1500 } 1501 1502 // isValuePreservedInRestore shouldn't change back to false if it has been set to true. 1503 return this.isValuePreservedInRestore || !overrideableByRestore; 1504 } 1505 } 1506 1507 /** 1508 * @return TRUE if a string is considered "binary" from KXML's point of view. NOTE DO NOT 1509 * pass null. 1510 */ isBinary(String s)1511 public static boolean isBinary(String s) { 1512 if (s == null) { 1513 throw new NullPointerException(); 1514 } 1515 // See KXmlSerializer.writeEscaped 1516 for (int i = 0; i < s.length(); i++) { 1517 char c = s.charAt(i); 1518 boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 1519 if (!allowedInXml) { 1520 return true; 1521 } 1522 } 1523 return false; 1524 } 1525 base64Encode(String s)1526 private static String base64Encode(String s) { 1527 return Base64.encodeToString(toBytes(s), Base64.NO_WRAP); 1528 } 1529 base64Decode(String s)1530 private static String base64Decode(String s) { 1531 return fromBytes(Base64.decode(s, Base64.DEFAULT)); 1532 } 1533 1534 // Note the followings are basically just UTF-16 encode/decode. But we want to preserve 1535 // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves, 1536 // since I don't know how Charset would treat them. 1537 toBytes(String s)1538 private static byte[] toBytes(String s) { 1539 final byte[] result = new byte[s.length() * 2]; 1540 int resultIndex = 0; 1541 for (int i = 0; i < s.length(); ++i) { 1542 char ch = s.charAt(i); 1543 result[resultIndex++] = (byte) (ch >> 8); 1544 result[resultIndex++] = (byte) ch; 1545 } 1546 return result; 1547 } 1548 fromBytes(byte[] bytes)1549 private static String fromBytes(byte[] bytes) { 1550 final StringBuilder sb = new StringBuilder(bytes.length / 2); 1551 1552 final int last = bytes.length - 1; 1553 1554 for (int i = 0; i < last; i += 2) { 1555 final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff)); 1556 sb.append(ch); 1557 } 1558 return sb.toString(); 1559 } 1560 1561 // Cache the list of names of system packages. This is only called once on system boot. cacheSystemPackageNamesAndSystemSignature(@onNull Context context)1562 public static void cacheSystemPackageNamesAndSystemSignature(@NonNull Context context) { 1563 final PackageManager packageManager = context.getPackageManager(); 1564 final long identity = Binder.clearCallingIdentity(); 1565 try { 1566 sSystemPackages.add(SYSTEM_PACKAGE_NAME); 1567 // Cache SetupWizard package name. 1568 final String setupWizPackageName = packageManager.getSetupWizardPackageName(); 1569 if (setupWizPackageName != null) { 1570 sSystemPackages.add(setupWizPackageName); 1571 } 1572 final List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0); 1573 final int installedPackagesCount = packageInfos.size(); 1574 for (int i = 0; i < installedPackagesCount; i++) { 1575 if (shouldAddToSystemPackages(packageInfos.get(i))) { 1576 sSystemPackages.add(packageInfos.get(i).packageName); 1577 } 1578 } 1579 } finally { 1580 Binder.restoreCallingIdentity(identity); 1581 } 1582 } 1583 shouldAddToSystemPackages(@onNull PackageInfo packageInfo)1584 private static boolean shouldAddToSystemPackages(@NonNull PackageInfo packageInfo) { 1585 // Shell and Root are not considered a part of the system 1586 if (isShellOrRoot(packageInfo.packageName)) { 1587 return false; 1588 } 1589 // Already added 1590 if (sSystemPackages.contains(packageInfo.packageName)) { 1591 return false; 1592 } 1593 return isSystemPackage(packageInfo.applicationInfo); 1594 } 1595 isShellOrRoot(@onNull String packageName)1596 private static boolean isShellOrRoot(@NonNull String packageName) { 1597 return (SHELL_PACKAGE_NAME.equals(packageName) 1598 || ROOT_PACKAGE_NAME.equals(packageName)); 1599 } 1600 isCalledFromSystem(@onNull String packageName)1601 private static boolean isCalledFromSystem(@NonNull String packageName) { 1602 // Shell and Root are not considered a part of the system 1603 if (isShellOrRoot(packageName)) { 1604 return false; 1605 } 1606 final int callingUid = Binder.getCallingUid(); 1607 // Native services running as a special UID get a pass 1608 final int callingAppId = UserHandle.getAppId(callingUid); 1609 return (callingAppId < FIRST_APPLICATION_UID); 1610 } 1611 isSystemPackage(@onNull Context context, @NonNull String packageName)1612 public static boolean isSystemPackage(@NonNull Context context, @NonNull String packageName) { 1613 // Check shell or root before trying to retrieve ApplicationInfo to fail fast 1614 if (isShellOrRoot(packageName)) { 1615 return false; 1616 } 1617 // If it's a known system package or known to be platform signed 1618 if (sSystemPackages.contains(packageName)) { 1619 return true; 1620 } 1621 1622 ApplicationInfo aInfo = null; 1623 final long identity = Binder.clearCallingIdentity(); 1624 try { 1625 try { 1626 // Notice that this makes a call to package manager inside the lock 1627 aInfo = context.getPackageManager().getApplicationInfo(packageName, 0); 1628 } catch (PackageManager.NameNotFoundException ignored) { 1629 } 1630 } finally { 1631 Binder.restoreCallingIdentity(identity); 1632 } 1633 return isSystemPackage(aInfo); 1634 } 1635 isSystemPackage(@ullable ApplicationInfo aInfo)1636 private static boolean isSystemPackage(@Nullable ApplicationInfo aInfo) { 1637 if (aInfo == null) { 1638 return false; 1639 } 1640 // If the system or a special system UID (like telephony), done. 1641 if (aInfo.uid < FIRST_APPLICATION_UID) { 1642 return true; 1643 } 1644 // If a persistent system app, done. 1645 if ((aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 1646 && (aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 1647 return true; 1648 } 1649 // Platform signed packages are considered to be from the system 1650 if (aInfo.isSignedWithPlatformKey()) { 1651 return true; 1652 } 1653 return false; 1654 } 1655 1656 @VisibleForTesting getMemoryUsage(String packageName)1657 public int getMemoryUsage(String packageName) { 1658 synchronized (mLock) { 1659 return mPackageToMemoryUsage.getOrDefault(packageName, 0); 1660 } 1661 } 1662 } 1663