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