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