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 static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon; 22 23 import android.aconfig.Aconfig.flag_permission; 24 import android.aconfig.Aconfig.flag_state; 25 import android.aconfig.Aconfig.parsed_flag; 26 import android.aconfig.Aconfig.parsed_flags; 27 import android.aconfigd.AconfigdFlagInfo; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.content.Context; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageManager; 34 import android.os.Binder; 35 import android.os.Build; 36 import android.os.FileUtils; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.SystemClock; 41 import android.os.UserHandle; 42 import android.provider.Settings; 43 import android.provider.Settings.Global; 44 import android.providers.settings.SettingsOperationProto; 45 import android.text.TextUtils; 46 import android.util.ArrayMap; 47 import android.util.ArraySet; 48 import android.util.AtomicFile; 49 import android.util.Base64; 50 import android.util.Slog; 51 import android.util.TimeUtils; 52 import android.util.Xml; 53 import android.util.proto.ProtoOutputStream; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.util.ArrayUtils; 58 import com.android.internal.util.FrameworkStatsLog; 59 import com.android.modules.utils.TypedXmlPullParser; 60 import com.android.modules.utils.TypedXmlSerializer; 61 62 import libcore.io.IoUtils; 63 64 import org.xmlpull.v1.XmlPullParser; 65 import org.xmlpull.v1.XmlPullParserException; 66 67 import java.io.File; 68 import java.io.FileInputStream; 69 import java.io.FileNotFoundException; 70 import java.io.FileOutputStream; 71 import java.io.IOException; 72 import java.io.PrintWriter; 73 import java.nio.file.Files; 74 import java.nio.file.Path; 75 import java.util.ArrayList; 76 import java.util.HashMap; 77 import java.util.HashSet; 78 import java.util.Iterator; 79 import java.util.LinkedList; 80 import java.util.List; 81 import java.util.Map; 82 import java.util.Objects; 83 import java.util.Set; 84 import java.util.concurrent.CountDownLatch; 85 86 /** 87 * This class contains the state for one type of settings. It is responsible 88 * for saving the state asynchronously to an XML file after a mutation and 89 * loading the from an XML file on construction. 90 * <p> 91 * This class uses the same lock as the settings provider to ensure that 92 * multiple changes made by the settings provider, e,g, upgrade, bulk insert, 93 * etc, are atomically persisted since the asynchronous persistence is using 94 * the same lock to grab the current state to write to disk. 95 * </p> 96 */ 97 public class SettingsState { 98 private static final boolean DEBUG = false; 99 private static final boolean DEBUG_PERSISTENCE = false; 100 101 private static final String LOG_TAG = "SettingsState"; 102 103 static final String SYSTEM_PACKAGE_NAME = "android"; 104 105 static final int SETTINGS_VERSION_NEW_ENCODING = 121; 106 107 // LINT.IfChange 108 public static final int MAX_LENGTH_PER_STRING = 32768; 109 // LINT.ThenChange(/services/core/java/com/android/server/audio/AudioDeviceInventory.java:settings_max_length_per_string) 110 private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; 111 private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000; 112 113 public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1; 114 public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 40000; 115 116 public static final int VERSION_UNDEFINED = -1; 117 118 public static final String FALLBACK_FILE_SUFFIX = ".fallback"; 119 120 private static final String TAG_SETTINGS = "settings"; 121 private static final String TAG_SETTING = "setting"; 122 private static final String ATTR_PACKAGE = "package"; 123 private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet"; 124 private static final String ATTR_TAG = "tag"; 125 private static final String ATTR_TAG_BASE64 = "tagBase64"; 126 127 private static final String ATTR_VERSION = "version"; 128 private static final String ATTR_ID = "id"; 129 private static final String ATTR_NAME = "name"; 130 131 private static final String TAG_NAMESPACE_HASHES = "namespaceHashes"; 132 private static final String TAG_NAMESPACE_HASH = "namespaceHash"; 133 private static final String ATTR_NAMESPACE = "namespace"; 134 private static final String ATTR_BANNED_HASH = "bannedHash"; 135 136 private static final String ATTR_PRESERVE_IN_RESTORE = "preserve_in_restore"; 137 138 /** 139 * Non-binary value will be written in this attributes. 140 */ 141 private static final String ATTR_VALUE = "value"; 142 private static final String ATTR_DEFAULT_VALUE = "defaultValue"; 143 144 /** 145 * KXmlSerializer won't like some characters. We encode such characters 146 * in base64 and store in this attribute. 147 * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64. 148 */ 149 private static final String ATTR_VALUE_BASE64 = "valueBase64"; 150 private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; 151 152 /** 153 * In the config table, there are special flags of the form {@code staged/namespace*flagName}. 154 * On boot, when the XML file is initially parsed, these transform into 155 * {@code namespace/flagName}, and the special staged flags are deleted. 156 */ 157 private static final String CONFIG_STAGED_PREFIX = "staged/"; 158 159 private static final List<String> sAconfigTextProtoFilesOnDevice = List.of( 160 "/system/etc/aconfig_flags.pb", 161 "/system_ext/etc/aconfig_flags.pb", 162 "/product/etc/aconfig_flags.pb", 163 "/vendor/etc/aconfig_flags.pb"); 164 165 private static final String APEX_DIR = "/apex"; 166 private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb"; 167 168 /** 169 * This tag is applied to all aconfig default value-loaded flags. 170 */ 171 private static final String BOOT_LOADED_DEFAULT_TAG = "BOOT_LOADED_DEFAULT"; 172 173 // This was used in version 120 and before. 174 private static final String NULL_VALUE_OLD_STYLE = "null"; 175 176 private static final int HISTORICAL_OPERATION_COUNT = 20; 177 private static final String HISTORICAL_OPERATION_UPDATE = "update"; 178 private static final String HISTORICAL_OPERATION_DELETE = "delete"; 179 private static final String HISTORICAL_OPERATION_PERSIST = "persist"; 180 private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize"; 181 private static final String HISTORICAL_OPERATION_RESET = "reset"; 182 183 private static final String SHELL_PACKAGE_NAME = "com.android.shell"; 184 private static final String ROOT_PACKAGE_NAME = "root"; 185 186 private static final String NULL_VALUE = "null"; 187 188 // TOBO(b/312444587): remove after Test Mission 2. 189 // Bulk sync names 190 private static final String BULK_SYNC_MARKER = "aconfigd_marker/bulk_synced"; 191 private static final String BULK_SYNC_TRIGGER_COUNTER = 192 "core_experiments_team_internal/BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter"; 193 194 private static final ArraySet<String> sSystemPackages = new ArraySet<>(); 195 196 private final Object mWriteLock = new Object(); 197 198 private final Object mLock; 199 200 private final Handler mHandler; 201 202 @GuardedBy("mLock") 203 private final Context mContext; 204 205 @GuardedBy("mLock") 206 private final ArrayMap<String, Setting> mSettings = new ArrayMap<>(); 207 208 @GuardedBy("mLock") 209 private final ArrayMap<String, String> mNamespaceBannedHashes = new ArrayMap<>(); 210 211 @GuardedBy("mLock") 212 private final ArrayMap<String, Integer> mPackageToMemoryUsage; 213 214 @GuardedBy("mLock") 215 private final int mMaxBytesPerAppPackage; 216 217 @GuardedBy("mLock") 218 private final File mStatePersistFile; 219 220 @GuardedBy("mLock") 221 private final String mStatePersistTag; 222 223 private final Setting mNullSetting = new Setting(null, null, false, null, null) { 224 @Override 225 public boolean isNull() { 226 return true; 227 } 228 }; 229 230 @GuardedBy("mLock") 231 private final List<HistoricalOperation> mHistoricalOperations; 232 233 @GuardedBy("mLock") 234 public final int mKey; 235 236 @GuardedBy("mLock") 237 private int mVersion = VERSION_UNDEFINED; 238 239 @GuardedBy("mLock") 240 private long mLastNotWrittenMutationTimeMillis; 241 242 @GuardedBy("mLock") 243 private boolean mDirty; 244 245 @GuardedBy("mLock") 246 private boolean mWriteScheduled; 247 248 @GuardedBy("mLock") 249 private long mNextId; 250 251 @GuardedBy("mLock") 252 private int mNextHistoricalOpIdx; 253 254 @GuardedBy("mLock") 255 @NonNull 256 private Map<String, Map<String, String>> mNamespaceDefaults; 257 258 // TOBO(b/312444587): remove the comparison logic after Test Mission 2. 259 @NonNull 260 private Map<String, AconfigdFlagInfo> mAconfigDefaultFlags; 261 262 public static final int SETTINGS_TYPE_GLOBAL = 0; 263 public static final int SETTINGS_TYPE_SYSTEM = 1; 264 public static final int SETTINGS_TYPE_SECURE = 2; 265 public static final int SETTINGS_TYPE_SSAID = 3; 266 public static final int SETTINGS_TYPE_CONFIG = 4; 267 268 public static final int SETTINGS_TYPE_MASK = 0xF0000000; 269 public static final int SETTINGS_TYPE_SHIFT = 28; 270 makeKey(int type, int userId)271 public static int makeKey(int type, int userId) { 272 return (type << SETTINGS_TYPE_SHIFT) | userId; 273 } 274 getTypeFromKey(int key)275 public static int getTypeFromKey(int key) { 276 return key >>> SETTINGS_TYPE_SHIFT; 277 } 278 getUserIdFromKey(int key)279 public static int getUserIdFromKey(int key) { 280 return key & ~SETTINGS_TYPE_MASK; 281 } 282 settingTypeToString(int type)283 public static String settingTypeToString(int type) { 284 switch (type) { 285 case SETTINGS_TYPE_CONFIG: { 286 return "SETTINGS_CONFIG"; 287 } 288 case SETTINGS_TYPE_GLOBAL: { 289 return "SETTINGS_GLOBAL"; 290 } 291 case SETTINGS_TYPE_SECURE: { 292 return "SETTINGS_SECURE"; 293 } 294 case SETTINGS_TYPE_SYSTEM: { 295 return "SETTINGS_SYSTEM"; 296 } 297 case SETTINGS_TYPE_SSAID: { 298 return "SETTINGS_SSAID"; 299 } 300 default: { 301 return "UNKNOWN"; 302 } 303 } 304 } 305 isConfigSettingsKey(int key)306 public static boolean isConfigSettingsKey(int key) { 307 return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG; 308 } 309 isGlobalSettingsKey(int key)310 public static boolean isGlobalSettingsKey(int key) { 311 return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL; 312 } 313 isSystemSettingsKey(int key)314 public static boolean isSystemSettingsKey(int key) { 315 return getTypeFromKey(key) == SETTINGS_TYPE_SYSTEM; 316 } 317 isSecureSettingsKey(int key)318 public static boolean isSecureSettingsKey(int key) { 319 return getTypeFromKey(key) == SETTINGS_TYPE_SECURE; 320 } 321 isSsaidSettingsKey(int key)322 public static boolean isSsaidSettingsKey(int key) { 323 return getTypeFromKey(key) == SETTINGS_TYPE_SSAID; 324 } 325 keyToString(int key)326 public static String keyToString(int key) { 327 return "Key[user=" + getUserIdFromKey(key) + ";type=" 328 + settingTypeToString(getTypeFromKey(key)) + "]"; 329 } 330 SettingsState( Context context, Object lock, File file, int key, int maxBytesPerAppPackage, Looper looper)331 public SettingsState( 332 Context context, 333 Object lock, 334 File file, 335 int key, 336 int maxBytesPerAppPackage, 337 Looper looper) { 338 // It is important that we use the same lock as the settings provider 339 // to ensure multiple mutations on this state are atomically persisted 340 // as the async persistence should be blocked while we make changes. 341 mContext = context; 342 mLock = lock; 343 mStatePersistFile = file; 344 mStatePersistTag = "settings-" + getTypeFromKey(key) + "-" + getUserIdFromKey(key); 345 mKey = key; 346 mHandler = new MyHandler(looper); 347 if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) { 348 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 349 mPackageToMemoryUsage = new ArrayMap<>(); 350 } else { 351 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 352 mPackageToMemoryUsage = null; 353 } 354 355 mHistoricalOperations = 356 Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; 357 358 mNamespaceDefaults = new HashMap<>(); 359 mAconfigDefaultFlags = new HashMap<>(); 360 361 ProtoOutputStream requests = null; 362 363 synchronized (mLock) { 364 readStateSyncLocked(); 365 366 if (Flags.loadAconfigDefaults()) { 367 if (isConfigSettingsKey(mKey)) { 368 loadAconfigDefaultValuesLocked(sAconfigTextProtoFilesOnDevice); 369 } 370 } 371 372 if (Flags.loadApexAconfigProtobufs()) { 373 if (isConfigSettingsKey(mKey)) { 374 List<String> apexProtoPaths = listApexProtoPaths(); 375 loadAconfigDefaultValuesLocked(apexProtoPaths); 376 } 377 } 378 379 if (enableAconfigStorageDaemon()) { 380 if (isConfigSettingsKey(mKey)) { 381 getAllAconfigFlagsFromSettings(mAconfigDefaultFlags); 382 } 383 } 384 } 385 } 386 387 @GuardedBy("mLock") getAllAconfigFlagsFromSettings( @onNull Map<String, AconfigdFlagInfo> flagInfoDefault)388 public int getAllAconfigFlagsFromSettings( 389 @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) { 390 Map<String, AconfigdFlagInfo> ret = new HashMap<>(); 391 int numSettings = mSettings.size(); 392 int num_requests = 0; 393 for (int i = 0; i < numSettings; i++) { 394 String name = mSettings.keyAt(i); 395 Setting setting = mSettings.valueAt(i); 396 AconfigdFlagInfo flag = 397 getFlagOverrideToSync(name, setting.getValue(), flagInfoDefault); 398 if (flag == null) { 399 continue; 400 } 401 if (flag.getIsReadWrite()) { 402 ++num_requests; 403 } 404 } 405 Slog.i(LOG_TAG, num_requests + " flag override requests created"); 406 return num_requests; 407 } 408 409 // TODO(b/341764371): migrate aconfig flag push to GMS core 410 @VisibleForTesting 411 @GuardedBy("mLock") 412 @Nullable getFlagOverrideToSync( String name, String value, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault)413 public AconfigdFlagInfo getFlagOverrideToSync( 414 String name, String value, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) { 415 int slashIdx = name.indexOf("/"); 416 if (slashIdx <= 0 || slashIdx >= name.length() - 1) { 417 Slog.e(LOG_TAG, "invalid flag name " + name); 418 return null; 419 } 420 421 String namespace = name.substring(0, slashIdx); 422 namespace = namespace.intern(); // Many configs have the same namespace. 423 String fullFlagName = name.substring(slashIdx + 1); 424 boolean isLocal = false; 425 426 // get actual fully qualified flag name <package>.<flag>, note this is done 427 // after staged flag is applied, so no need to check staged flags 428 if (namespace.equals("device_config_overrides")) { 429 int colonIdx = fullFlagName.indexOf(":"); 430 if (colonIdx == -1) { 431 Slog.e(LOG_TAG, "invalid local override flag name " + name); 432 return null; 433 } 434 namespace = fullFlagName.substring(0, colonIdx); 435 fullFlagName = fullFlagName.substring(colonIdx + 1); 436 isLocal = true; 437 } 438 // get package name and flag name 439 int dotIdx = fullFlagName.lastIndexOf("."); 440 if (dotIdx == -1) { 441 Slog.e(LOG_TAG, "invalid override flag name " + name); 442 return null; 443 } 444 AconfigdFlagInfo flag = flagInfoDefault.get(fullFlagName); 445 if (flag == null || !namespace.equals(flag.getNamespace())) { 446 return null; 447 } 448 449 if (isLocal) { 450 flag.setLocalFlagValue(value); 451 } else { 452 flag.setServerFlagValue(value); 453 } 454 return flag; 455 } 456 457 @GuardedBy("mLock") loadAconfigDefaultValuesLocked(List<String> filePaths)458 private void loadAconfigDefaultValuesLocked(List<String> filePaths) { 459 for (String fileName : filePaths) { 460 File f = new File(fileName); 461 if (f.isFile() && f.canRead()) { 462 try (FileInputStream inputStream = new FileInputStream(fileName)) { 463 loadAconfigDefaultValues( 464 inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags); 465 } catch (IOException e) { 466 Slog.e(LOG_TAG, "failed to read protobuf", e); 467 } 468 } else { 469 Slog.d(LOG_TAG, "No protobuf file at " + fileName); 470 } 471 } 472 } 473 listApexProtoPaths()474 private List<String> listApexProtoPaths() { 475 LinkedList<String> paths = new LinkedList(); 476 477 File apexDirectory = new File(APEX_DIR); 478 if (!apexDirectory.isDirectory()) { 479 return paths; 480 } 481 482 File[] subdirs = apexDirectory.listFiles(); 483 if (subdirs == null) { 484 return paths; 485 } 486 487 for (File prefix : subdirs) { 488 // For each mainline modules, there are two directories, one <modulepackage>/, 489 // and one <modulepackage>@<versioncode>/. Just read the former. 490 if (prefix.getAbsolutePath().contains("@")) { 491 continue; 492 } 493 494 File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX); 495 if (!protoPath.exists()) { 496 continue; 497 } 498 499 paths.add(protoPath.getAbsolutePath()); 500 } 501 return paths; 502 } 503 504 @VisibleForTesting 505 @GuardedBy("mLock") addAconfigDefaultValuesFromMap( @onNull Map<String, Map<String, String>> defaultMap)506 public void addAconfigDefaultValuesFromMap( 507 @NonNull Map<String, Map<String, String>> defaultMap) { 508 mNamespaceDefaults.putAll(defaultMap); 509 } 510 511 @VisibleForTesting 512 @GuardedBy("mLock") loadAconfigDefaultValues( byte[] fileContents, @NonNull Map<String, Map<String, String>> defaultMap, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault)513 public static void loadAconfigDefaultValues( 514 byte[] fileContents, 515 @NonNull Map<String, Map<String, String>> defaultMap, 516 @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) { 517 try { 518 parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents); 519 for (parsed_flag flag : parsedFlags.getParsedFlagList()) { 520 if (!defaultMap.containsKey(flag.getNamespace())) { 521 Map<String, String> defaults = new HashMap<>(); 522 defaultMap.put(flag.getNamespace(), defaults); 523 } 524 String fullFlagName = flag.getPackage() + "." + flag.getName(); 525 String flagName = flag.getNamespace() + "/" + fullFlagName; 526 String flagValue = flag.getState() == flag_state.ENABLED ? "true" : "false"; 527 boolean isReadWrite = flag.getPermission() == flag_permission.READ_WRITE; 528 defaultMap.get(flag.getNamespace()).put(flagName, flagValue); 529 if (!flagInfoDefault.containsKey(fullFlagName)) { 530 flagInfoDefault.put( 531 fullFlagName, 532 AconfigdFlagInfo.newBuilder() 533 .setPackageName(flag.getPackage()) 534 .setFlagName(flag.getName()) 535 .setDefaultFlagValue(flagValue) 536 .setIsReadWrite(isReadWrite) 537 .setNamespace(flag.getNamespace()) 538 .build()); 539 } 540 } 541 } catch (IOException e) { 542 Slog.e(LOG_TAG, "failed to parse protobuf", e); 543 } 544 } 545 546 // The settings provider must hold its lock when calling here. 547 @GuardedBy("mLock") getVersionLocked()548 public int getVersionLocked() { 549 return mVersion; 550 } 551 getNullSetting()552 public Setting getNullSetting() { 553 return mNullSetting; 554 } 555 556 // The settings provider must hold its lock when calling here. 557 @GuardedBy("mLock") setVersionLocked(int version)558 public void setVersionLocked(int version) { 559 if (version == mVersion) { 560 return; 561 } 562 mVersion = version; 563 564 scheduleWriteIfNeededLocked(); 565 } 566 567 // The settings provider must hold its lock when calling here. 568 @GuardedBy("mLock") removeSettingsForPackageLocked(String packageName)569 public void removeSettingsForPackageLocked(String packageName) { 570 final int settingCount = mSettings.size(); 571 for (int i = settingCount - 1; i >= 0; i--) { 572 String name = mSettings.keyAt(i); 573 // Settings defined by us are never dropped. 574 if (Settings.System.PUBLIC_SETTINGS.contains(name) 575 || Settings.System.PRIVATE_SETTINGS.contains(name)) { 576 continue; 577 } 578 Setting setting = mSettings.valueAt(i); 579 if (packageName.equals(setting.packageName)) { 580 deleteSettingLocked(setting.name); 581 } 582 } 583 } 584 585 // The settings provider must hold its lock when calling here. 586 @GuardedBy("mLock") getSettingNamesLocked()587 public List<String> getSettingNamesLocked() { 588 ArrayList<String> names = new ArrayList<>(); 589 final int settingsCount = mSettings.size(); 590 for (int i = 0; i < settingsCount; i++) { 591 String name = mSettings.keyAt(i); 592 names.add(name); 593 } 594 return names; 595 } 596 597 @NonNull getAconfigDefaultValues()598 public Map<String, Map<String, String>> getAconfigDefaultValues() { 599 synchronized (mLock) { 600 return mNamespaceDefaults; 601 } 602 } 603 604 @NonNull getAconfigDefaultFlags()605 public Map<String, AconfigdFlagInfo> getAconfigDefaultFlags() { 606 synchronized (mLock) { 607 return mAconfigDefaultFlags; 608 } 609 } 610 611 // The settings provider must hold its lock when calling here. getSettingLocked(String name)612 public Setting getSettingLocked(String name) { 613 if (TextUtils.isEmpty(name)) { 614 return mNullSetting; 615 } 616 Setting setting = mSettings.get(name); 617 if (setting != null) { 618 return new Setting(setting); 619 } 620 return mNullSetting; 621 } 622 623 // The settings provider must hold its lock when calling here. updateSettingLocked(String name, String value, String tag, boolean makeValue, String packageName)624 public boolean updateSettingLocked(String name, String value, String tag, 625 boolean makeValue, String packageName) { 626 if (!hasSettingLocked(name)) { 627 return false; 628 } 629 630 return insertSettingLocked(name, value, tag, makeValue, packageName); 631 } 632 633 // The settings provider must hold its lock when calling here. 634 @GuardedBy("mLock") resetSettingDefaultValueLocked(String name)635 public void resetSettingDefaultValueLocked(String name) { 636 Setting oldSetting = getSettingLocked(name); 637 if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) { 638 String oldValue = oldSetting.getValue(); 639 String oldDefaultValue = oldSetting.getDefaultValue(); 640 Setting newSetting = new Setting(name, oldSetting.getValue(), null, 641 oldSetting.getPackageName(), oldSetting.getTag(), false, 642 oldSetting.getId()); 643 int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0, 644 oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); 645 checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); 646 mSettings.put(name, newSetting); 647 updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); 648 scheduleWriteIfNeededLocked(); 649 } 650 } 651 652 // The settings provider must hold its lock when calling here. insertSettingOverrideableByRestoreLocked(String name, String value, String tag, boolean makeDefault, String packageName)653 public boolean insertSettingOverrideableByRestoreLocked(String name, String value, String tag, 654 boolean makeDefault, String packageName) { 655 return insertSettingLocked(name, value, tag, makeDefault, false, packageName, 656 /* overrideableByRestore */ true); 657 } 658 659 // The settings provider must hold its lock when calling here. 660 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, String packageName)661 public boolean insertSettingLocked(String name, String value, String tag, 662 boolean makeDefault, String packageName) { 663 return insertSettingLocked(name, value, tag, makeDefault, false, packageName, 664 /* overrideableByRestore */ false); 665 } 666 667 // The settings provider must hold its lock when calling here. 668 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName, boolean overrideableByRestore)669 public boolean insertSettingLocked(String name, String value, String tag, 670 boolean makeDefault, boolean forceNonSystemPackage, String packageName, 671 boolean overrideableByRestore) { 672 if (TextUtils.isEmpty(name)) { 673 return false; 674 } 675 676 // Aconfig flags are always boot stable, so we anytime we write one, we stage it to be 677 // applied on reboot. 678 if (Flags.stageAllAconfigFlags()) { 679 int slashIndex = name.indexOf("/"); 680 boolean stageFlag = isConfigSettingsKey(mKey) 681 && slashIndex != -1 682 && slashIndex != 0 683 && slashIndex != name.length(); 684 685 if (stageFlag) { 686 String namespace = name.substring(0, slashIndex); 687 String flag = name.substring(slashIndex + 1); 688 689 boolean isAconfig = mNamespaceDefaults.containsKey(namespace) 690 && mNamespaceDefaults.get(namespace).containsKey(name); 691 692 if (isAconfig) { 693 name = "staged/" + namespace + "*" + flag; 694 } 695 } 696 } 697 698 final boolean isNameTooLong = name.length() > SettingsState.MAX_LENGTH_PER_STRING; 699 final boolean isValueTooLong = 700 value != null && value.length() > SettingsState.MAX_LENGTH_PER_STRING; 701 if (isNameTooLong || isValueTooLong) { 702 // only print the first few bytes of the name in case it is long 703 final String errorMessage = "The " + (isNameTooLong ? "name" : "value") 704 + " of your setting [" 705 + (name.length() > 20 ? (name.substring(0, 20) + "...") : name) 706 + "] is too long. The max length allowed for the string is " 707 + MAX_LENGTH_PER_STRING + "."; 708 throw new IllegalArgumentException(errorMessage); 709 } 710 711 Setting oldState = mSettings.get(name); 712 String previousOwningPackage = (oldState != null) ? oldState.packageName : null; 713 // If the old state doesn't exist, no need to handle the owning package change 714 final boolean owningPackageChanged = previousOwningPackage != null 715 && !previousOwningPackage.equals(packageName); 716 717 String oldValue = (oldState != null) ? oldState.value : null; 718 String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; 719 String newDefaultValue = makeDefault ? value : oldDefaultValue; 720 721 int newSizeForCurrentPackage = getNewMemoryUsagePerPackageLocked(packageName, 722 /* deltaKeyLength= */ (oldState == null || owningPackageChanged) ? name.length() : 0, 723 /* oldValue= */ owningPackageChanged ? null : oldValue, 724 /* newValue= */ value, 725 /* oldDefaultValue= */ owningPackageChanged ? null : oldDefaultValue, 726 /* newDefaultValue = */ newDefaultValue); 727 // Only check the memory usage for the current package. Even if the owning package 728 // has changed, the previous owning package will only have a reduced memory usage, so 729 // there is no need to check its memory usage. 730 checkNewMemoryUsagePerPackageLocked(packageName, newSizeForCurrentPackage); 731 732 Setting newState; 733 734 if (oldState != null) { 735 if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage, 736 overrideableByRestore)) { 737 return false; 738 } 739 newState = oldState; 740 } else { 741 newState = new Setting(name, value, makeDefault, packageName, tag, 742 forceNonSystemPackage); 743 mSettings.put(name, newState); 744 } 745 746 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, value, newState.value, 747 oldValue, tag, makeDefault, getUserIdFromKey(mKey), 748 FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED); 749 750 addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); 751 752 updateMemoryUsagePerPackageLocked(packageName, newSizeForCurrentPackage); 753 754 if (owningPackageChanged) { 755 int newSizeForPreviousPackage = getNewMemoryUsagePerPackageLocked(previousOwningPackage, 756 /* deltaKeyLength= */ -name.length(), 757 /* oldValue= */ oldValue, 758 /* newValue= */ null, 759 /* oldDefaultValue= */ oldDefaultValue, 760 /* newDefaultValue = */ null); 761 updateMemoryUsagePerPackageLocked(previousOwningPackage, newSizeForPreviousPackage); 762 } 763 764 scheduleWriteIfNeededLocked(); 765 766 return true; 767 } 768 769 @GuardedBy("mLock") isNewConfigBannedLocked(String prefix, Map<String, String> keyValues)770 public boolean isNewConfigBannedLocked(String prefix, Map<String, String> keyValues) { 771 // Replaces old style "null" String values with actual null's. This is done to simulate 772 // what will happen to String "null" values when they are written to Settings. This needs to 773 // be done here make sure that config hash computed during is banned check matches the 774 // one computed during banning when values are already stored. 775 keyValues = removeNullValueOldStyle(keyValues); 776 String bannedHash = mNamespaceBannedHashes.get(prefix); 777 if (bannedHash == null) { 778 return false; 779 } 780 return bannedHash.equals(hashCode(keyValues)); 781 } 782 783 @GuardedBy("mLock") unbanAllConfigIfBannedConfigUpdatedLocked(String prefix)784 public void unbanAllConfigIfBannedConfigUpdatedLocked(String prefix) { 785 // If the prefix updated is a banned namespace, clear mNamespaceBannedHashes 786 // to unban all unbanned namespaces. 787 if (mNamespaceBannedHashes.get(prefix) != null) { 788 mNamespaceBannedHashes.clear(); 789 scheduleWriteIfNeededLocked(); 790 } 791 } 792 793 @GuardedBy("mLock") banConfigurationLocked(String prefix, Map<String, String> keyValues)794 public void banConfigurationLocked(String prefix, Map<String, String> keyValues) { 795 if (prefix == null || keyValues.isEmpty()) { 796 return; 797 } 798 // The write is intentionally not scheduled here, banned hashes should and will be written 799 // when the related setting changes are written 800 mNamespaceBannedHashes.put(prefix, hashCode(keyValues)); 801 } 802 803 @GuardedBy("mLock") getAllConfigPrefixesLocked()804 public Set<String> getAllConfigPrefixesLocked() { 805 Set<String> prefixSet = new HashSet<>(); 806 final int settingsCount = mSettings.size(); 807 for (int i = 0; i < settingsCount; i++) { 808 String name = mSettings.keyAt(i); 809 prefixSet.add(name.split("/")[0] + "/"); 810 } 811 return prefixSet; 812 } 813 814 // The settings provider must hold its lock when calling here. 815 // Returns the list of keys which changed (added, updated, or deleted). 816 @GuardedBy("mLock") setSettingsLocked(String prefix, Map<String, String> keyValues, String packageName)817 public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues, 818 String packageName) { 819 List<String> changedKeys = new ArrayList<>(); 820 final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator(); 821 int index = prefix.lastIndexOf('/'); 822 String namespace = index < 0 ? "" : prefix.substring(0, index); 823 Map<String, String> trunkFlagMap = (mNamespaceDefaults == null) 824 ? null : mNamespaceDefaults.get(namespace); 825 // Delete old keys with the prefix that are not part of the new set. 826 // trunk flags will not be configured with restricted propagation 827 // trunk flags will be explicitly set, so not removing them here 828 while (iterator.hasNext()) { 829 Map.Entry<String, Setting> entry = iterator.next(); 830 final String key = entry.getKey(); 831 final Setting oldState = entry.getValue(); 832 if (key != null && (trunkFlagMap == null || !trunkFlagMap.containsKey(key)) 833 && key.startsWith(prefix) && !keyValues.containsKey(key)) { 834 iterator.remove(); 835 836 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, 837 /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, 838 getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); 839 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); 840 changedKeys.add(key); // key was removed 841 } 842 } 843 844 // Update/add new keys 845 for (String key : keyValues.keySet()) { 846 String value = keyValues.get(key); 847 848 // Rename key if it's an aconfig flag. 849 String flagName = key; 850 if (Flags.stageAllAconfigFlags() && isConfigSettingsKey(mKey)) { 851 int slashIndex = flagName.indexOf("/"); 852 boolean stageFlag = slashIndex > 0 && slashIndex != flagName.length(); 853 boolean isAconfig = trunkFlagMap != null && trunkFlagMap.containsKey(flagName); 854 if (stageFlag && isAconfig) { 855 String flagWithoutNamespace = flagName.substring(slashIndex + 1); 856 flagName = "staged/" + namespace + "*" + flagWithoutNamespace; 857 } 858 } 859 860 String oldValue = null; 861 Setting state = mSettings.get(flagName); 862 if (state == null) { 863 state = new Setting(flagName, value, false, packageName, null); 864 mSettings.put(flagName, state); 865 changedKeys.add(flagName); // key was added 866 } else if (state.value != value) { 867 oldValue = state.value; 868 state.update(value, false, packageName, null, true, 869 /* overrideableByRestore */ false); 870 changedKeys.add(flagName); // key was updated 871 } else { 872 // this key/value already exists, no change and no logging necessary 873 continue; 874 } 875 876 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, flagName, value, state.value, 877 oldValue, /* tag */ null, /* make default */ false, 878 getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED); 879 addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, state); 880 } 881 882 if (!changedKeys.isEmpty()) { 883 scheduleWriteIfNeededLocked(); 884 } 885 886 return changedKeys; 887 } 888 889 // The settings provider must hold its lock when calling here. 890 public void persistSettingsLocked() { 891 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 892 // schedule a write operation right away 893 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget(); 894 } 895 896 // The settings provider must hold its lock when calling here. 897 @GuardedBy("mLock") 898 public boolean deleteSettingLocked(String name) { 899 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 900 return false; 901 } 902 903 Setting oldState = mSettings.remove(name); 904 if (oldState == null) { 905 return false; 906 } 907 int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, 908 -name.length() /* deltaKeySize */, 909 oldState.value, null, oldState.defaultValue, null); 910 911 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", 912 /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), 913 FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); 914 915 updateMemoryUsagePerPackageLocked(oldState.packageName, newSize); 916 917 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); 918 919 scheduleWriteIfNeededLocked(); 920 921 return true; 922 } 923 924 // The settings provider must hold its lock when calling here. 925 @GuardedBy("mLock") 926 public boolean resetSettingLocked(String name) { 927 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 928 return false; 929 } 930 931 Setting setting = mSettings.get(name); 932 if (setting == null) { 933 return false; 934 } 935 936 Setting oldSetting = new Setting(setting); 937 String oldValue = setting.getValue(); 938 String oldDefaultValue = setting.getDefaultValue(); 939 940 int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue, 941 oldDefaultValue, oldDefaultValue, oldDefaultValue); 942 checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); 943 944 if (!setting.reset()) { 945 return false; 946 } 947 948 updateMemoryUsagePerPackageLocked(setting.packageName, newSize); 949 950 addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); 951 952 scheduleWriteIfNeededLocked(); 953 954 return true; 955 } 956 957 // The settings provider must hold its lock when calling here. 958 @GuardedBy("mLock") 959 public void destroyLocked(Runnable callback) { 960 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 961 if (callback != null) { 962 if (mDirty) { 963 // Do it without a delay. 964 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS, 965 callback).sendToTarget(); 966 return; 967 } 968 callback.run(); 969 } 970 } 971 972 @GuardedBy("mLock") 973 private void addHistoricalOperationLocked(String type, Setting setting) { 974 if (mHistoricalOperations == null) { 975 return; 976 } 977 HistoricalOperation operation = new HistoricalOperation( 978 System.currentTimeMillis(), type, 979 setting != null ? new Setting(setting) : null); 980 if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) { 981 mHistoricalOperations.add(operation); 982 } else { 983 mHistoricalOperations.set(mNextHistoricalOpIdx, operation); 984 } 985 mNextHistoricalOpIdx++; 986 if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) { 987 mNextHistoricalOpIdx = 0; 988 } 989 } 990 991 /** 992 * Dump historical operations as a proto buf. 993 * 994 * @param proto The proto buf stream to dump to 995 * @param fieldId The repeated field ID to use to save an operation to. 996 */ 997 void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) { 998 synchronized (mLock) { 999 if (mHistoricalOperations == null) { 1000 return; 1001 } 1002 1003 final int operationCount = mHistoricalOperations.size(); 1004 for (int i = 0; i < operationCount; i++) { 1005 int index = mNextHistoricalOpIdx - 1 - i; 1006 if (index < 0) { 1007 index = operationCount + index; 1008 } 1009 HistoricalOperation operation = mHistoricalOperations.get(index); 1010 1011 final long token = proto.start(fieldId); 1012 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp); 1013 proto.write(SettingsOperationProto.OPERATION, operation.mOperation); 1014 if (operation.mSetting != null) { 1015 // Only add the name of the setting, since we don't know the historical package 1016 // and values for it so they would be misleading to add here (all we could 1017 // add is what the current data is). 1018 proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName()); 1019 } 1020 proto.end(token); 1021 } 1022 } 1023 } 1024 1025 public void dumpHistoricalOperations(PrintWriter pw) { 1026 synchronized (mLock) { 1027 if (mHistoricalOperations == null) { 1028 return; 1029 } 1030 pw.println("Historical operations"); 1031 final int operationCount = mHistoricalOperations.size(); 1032 for (int i = 0; i < operationCount; i++) { 1033 int index = mNextHistoricalOpIdx - 1 - i; 1034 if (index < 0) { 1035 index = operationCount + index; 1036 } 1037 HistoricalOperation operation = mHistoricalOperations.get(index); 1038 pw.print(TimeUtils.formatForLogging(operation.mTimestamp)); 1039 pw.print(" "); 1040 pw.print(operation.mOperation); 1041 if (operation.mSetting != null) { 1042 pw.print(" "); 1043 // Only print the name of the setting, since we don't know the 1044 // historical package and values for it so they would be misleading 1045 // to print here (all we could print is what the current data is). 1046 pw.print(operation.mSetting.getName()); 1047 } 1048 pw.println(); 1049 } 1050 pw.println(); 1051 pw.println(); 1052 } 1053 } 1054 1055 @GuardedBy("mLock") 1056 private boolean isExemptFromMemoryUsageCap(String packageName) { 1057 return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED 1058 || SYSTEM_PACKAGE_NAME.equals(packageName); 1059 } 1060 1061 @GuardedBy("mLock") 1062 private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize) 1063 throws IllegalStateException { 1064 if (isExemptFromMemoryUsageCap(packageName)) { 1065 return; 1066 } 1067 if (newSize > mMaxBytesPerAppPackage) { 1068 throw new IllegalStateException("You are adding too many system settings. " 1069 + "You should stop using system settings for app specific data" 1070 + " package: " + packageName); 1071 } 1072 } 1073 1074 @GuardedBy("mLock") 1075 private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeyLength, 1076 String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) { 1077 if (isExemptFromMemoryUsageCap(packageName)) { 1078 return 0; 1079 } 1080 final int currentSize = mPackageToMemoryUsage.getOrDefault(packageName, 0); 1081 final int oldValueLength = (oldValue != null) ? oldValue.length() : 0; 1082 final int newValueLength = (newValue != null) ? newValue.length() : 0; 1083 final int oldDefaultValueLength = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; 1084 final int newDefaultValueLength = (newDefaultValue != null) ? newDefaultValue.length() : 0; 1085 final int deltaSize = (deltaKeyLength + newValueLength + newDefaultValueLength 1086 - oldValueLength - oldDefaultValueLength) * Character.BYTES; 1087 return Math.max(currentSize + deltaSize, 0); 1088 } 1089 1090 @GuardedBy("mLock") 1091 private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) { 1092 if (isExemptFromMemoryUsageCap(packageName)) { 1093 return; 1094 } 1095 if (DEBUG) { 1096 Slog.i(LOG_TAG, "Settings for package: " + packageName 1097 + " size: " + newSize + " bytes."); 1098 } 1099 mPackageToMemoryUsage.put(packageName, newSize); 1100 } 1101 1102 public boolean hasSetting(String name) { 1103 synchronized (mLock) { 1104 return hasSettingLocked(name); 1105 } 1106 } 1107 1108 @GuardedBy("mLock") 1109 private boolean hasSettingLocked(String name) { 1110 return mSettings.indexOfKey(name) >= 0; 1111 } 1112 1113 @GuardedBy("mLock") 1114 private void scheduleWriteIfNeededLocked() { 1115 // If dirty then we have a write already scheduled. 1116 if (!mDirty) { 1117 mDirty = true; 1118 writeStateAsyncLocked(); 1119 } 1120 } 1121 1122 @GuardedBy("mLock") 1123 private void writeStateAsyncLocked() { 1124 final long currentTimeMillis = SystemClock.uptimeMillis(); 1125 1126 if (mWriteScheduled) { 1127 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 1128 1129 // If enough time passed, write without holding off anymore. 1130 final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis 1131 - mLastNotWrittenMutationTimeMillis; 1132 if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) { 1133 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget(); 1134 return; 1135 } 1136 1137 // Hold off a bit more as settings are frequently changing. 1138 final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis 1139 + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0); 1140 final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis); 1141 1142 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 1143 mHandler.sendMessageDelayed(message, writeDelayMillis); 1144 } else { 1145 mLastNotWrittenMutationTimeMillis = currentTimeMillis; 1146 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 1147 mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS); 1148 mWriteScheduled = true; 1149 } 1150 } 1151 1152 private void doWriteState() { 1153 boolean wroteState = false; 1154 String settingFailedToBePersisted = null; 1155 final int version; 1156 final ArrayMap<String, Setting> settings; 1157 final ArrayMap<String, String> namespaceBannedHashes; 1158 1159 synchronized (mLock) { 1160 version = mVersion; 1161 settings = new ArrayMap<>(mSettings); 1162 namespaceBannedHashes = new ArrayMap<>(mNamespaceBannedHashes); 1163 mDirty = false; 1164 mWriteScheduled = false; 1165 } 1166 1167 synchronized (mWriteLock) { 1168 if (DEBUG_PERSISTENCE) { 1169 Slog.i(LOG_TAG, "[PERSIST START]"); 1170 } 1171 1172 AtomicFile destination = new AtomicFile(mStatePersistFile, mStatePersistTag); 1173 FileOutputStream out = null; 1174 try { 1175 out = destination.startWrite(); 1176 1177 TypedXmlSerializer serializer = Xml.resolveSerializer(out); 1178 serializer.startDocument(null, true); 1179 serializer.startTag(null, TAG_SETTINGS); 1180 serializer.attributeInt(null, ATTR_VERSION, version); 1181 1182 final int settingCount = settings.size(); 1183 for (int i = 0; i < settingCount; i++) { 1184 Setting setting = settings.valueAt(i); 1185 if (setting.isTransient()) { 1186 if (DEBUG_PERSISTENCE) { 1187 Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName()); 1188 } 1189 continue; 1190 } 1191 1192 try { 1193 if (writeSingleSetting( 1194 mVersion, 1195 serializer, 1196 Long.toString(setting.getId()), 1197 setting.getName(), 1198 setting.getValue(), setting.getDefaultValue(), 1199 setting.getPackageName(), 1200 setting.getTag(), setting.isDefaultFromSystem(), 1201 setting.isValuePreservedInRestore())) { 1202 if (DEBUG_PERSISTENCE) { 1203 Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" 1204 + setting.getValue()); 1205 } 1206 } 1207 } catch (IOException ex) { 1208 Slog.e(LOG_TAG, "[ABORT PERSISTING]" + setting.getName() 1209 + " due to error writing to disk", ex); 1210 // A setting failed to be written. Abort the serialization to avoid leaving 1211 // a partially serialized setting on disk, which can cause parsing errors. 1212 // Note down the problematic setting, so that we can delete it before trying 1213 // again to persist the rest of the settings. 1214 settingFailedToBePersisted = setting.getName(); 1215 throw ex; 1216 } 1217 } 1218 serializer.endTag(null, TAG_SETTINGS); 1219 1220 serializer.startTag(null, TAG_NAMESPACE_HASHES); 1221 for (int i = 0; i < namespaceBannedHashes.size(); i++) { 1222 String namespace = namespaceBannedHashes.keyAt(i); 1223 String bannedHash = namespaceBannedHashes.get(namespace); 1224 if (writeSingleNamespaceHash(serializer, namespace, bannedHash)) { 1225 if (DEBUG_PERSISTENCE) { 1226 Slog.i(LOG_TAG, "[PERSISTED] namespace=" + namespace 1227 + ", bannedHash=" + bannedHash); 1228 } 1229 } 1230 } 1231 serializer.endTag(null, TAG_NAMESPACE_HASHES); 1232 serializer.endDocument(); 1233 destination.finishWrite(out); 1234 1235 wroteState = true; 1236 1237 if (DEBUG_PERSISTENCE) { 1238 Slog.i(LOG_TAG, "[PERSIST END]"); 1239 } 1240 } catch (Throwable t) { 1241 Slog.e(LOG_TAG, "Failed to write settings, restoring old file", t); 1242 if (t instanceof IOException) { 1243 if (t.getMessage().contains("Couldn't create directory")) { 1244 if (DEBUG) { 1245 // we failed to create a directory, so log the permissions and existence 1246 // state for the settings file and directory 1247 logSettingsDirectoryInformation(destination.getBaseFile()); 1248 } 1249 // attempt to create the directory with Files.createDirectories, which 1250 // throws more informative errors than File.mkdirs. 1251 Path parentPath = destination.getBaseFile().getParentFile().toPath(); 1252 try { 1253 Files.createDirectories(parentPath); 1254 if (DEBUG) { 1255 Slog.i(LOG_TAG, "Successfully created " + parentPath); 1256 } 1257 } catch (Throwable t2) { 1258 Slog.e(LOG_TAG, "Failed to write " + parentPath 1259 + " with Files.writeDirectories", t2); 1260 } 1261 } 1262 } 1263 destination.failWrite(out); 1264 } finally { 1265 IoUtils.closeQuietly(out); 1266 } 1267 } 1268 1269 if (!wroteState) { 1270 if (settingFailedToBePersisted != null) { 1271 synchronized (mLock) { 1272 // Delete the problematic setting. This will schedule a write as well. 1273 deleteSettingLocked(settingFailedToBePersisted); 1274 } 1275 } 1276 } else { 1277 // success 1278 synchronized (mLock) { 1279 addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null); 1280 } 1281 } 1282 } 1283 1284 private static void logSettingsDirectoryInformation(File settingsFile) { 1285 File parent = settingsFile.getParentFile(); 1286 Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile 1287 + " with stacktrace ", new Exception()); 1288 File ancestorDir = parent; 1289 while (ancestorDir != null) { 1290 if (!ancestorDir.exists()) { 1291 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 1292 + " does not exist"); 1293 ancestorDir = ancestorDir.getParentFile(); 1294 } else { 1295 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 1296 + " exists"); 1297 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir 1298 + " permissions: r: " + ancestorDir.canRead() + " w: " 1299 + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute()); 1300 File ancestorParent = ancestorDir.getParentFile(); 1301 if (ancestorParent != null) { 1302 Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent 1303 + " permissions: r: " + ancestorParent.canRead() + " w: " 1304 + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute()); 1305 } 1306 break; 1307 } 1308 } 1309 } 1310 1311 static boolean writeSingleSetting(int version, TypedXmlSerializer serializer, String id, 1312 String name, String value, String defaultValue, String packageName, 1313 String tag, boolean defaultSysSet, boolean isValuePreservedInRestore) 1314 throws IOException { 1315 if (id == null || isBinary(id) || name == null || isBinary(name) 1316 || packageName == null || isBinary(packageName)) { 1317 if (DEBUG_PERSISTENCE) { 1318 Slog.w(LOG_TAG, "Invalid arguments for writeSingleSetting: version=" + version 1319 + ", id=" + id + ", name=" + name + ", value=" + value 1320 + ", defaultValue=" + defaultValue + ", packageName=" + packageName 1321 + ", tag=" + tag + ", defaultSysSet=" + defaultSysSet 1322 + ", isValuePreservedInRestore=" + isValuePreservedInRestore); 1323 } 1324 return false; 1325 } 1326 serializer.startTag(null, TAG_SETTING); 1327 serializer.attribute(null, ATTR_ID, id); 1328 serializer.attribute(null, ATTR_NAME, name); 1329 setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64, 1330 version, serializer, value); 1331 serializer.attribute(null, ATTR_PACKAGE, packageName); 1332 if (defaultValue != null) { 1333 setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64, 1334 version, serializer, defaultValue); 1335 serializer.attributeBoolean(null, ATTR_DEFAULT_SYS_SET, defaultSysSet); 1336 setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64, 1337 version, serializer, tag); 1338 } 1339 if (isValuePreservedInRestore) { 1340 serializer.attributeBoolean(null, ATTR_PRESERVE_IN_RESTORE, true); 1341 } 1342 serializer.endTag(null, TAG_SETTING); 1343 return true; 1344 } 1345 1346 static void setValueAttribute(String attr, String attrBase64, int version, 1347 TypedXmlSerializer serializer, String value) throws IOException { 1348 if (version >= SETTINGS_VERSION_NEW_ENCODING) { 1349 if (value == null) { 1350 // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64. 1351 } else if (isBinary(value)) { 1352 serializer.attribute(null, attrBase64, base64Encode(value)); 1353 } else { 1354 serializer.attribute(null, attr, value); 1355 } 1356 } else { 1357 // Old encoding. 1358 if (value == null) { 1359 serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE); 1360 } else { 1361 serializer.attribute(null, attr, value); 1362 } 1363 } 1364 } 1365 1366 private static boolean writeSingleNamespaceHash(TypedXmlSerializer serializer, String namespace, 1367 String bannedHashCode) throws IOException { 1368 if (namespace == null || bannedHashCode == null) { 1369 if (DEBUG_PERSISTENCE) { 1370 Slog.w(LOG_TAG, "Invalid arguments for writeSingleNamespaceHash: namespace=" 1371 + namespace + ", bannedHashCode=" + bannedHashCode); 1372 } 1373 return false; 1374 } 1375 serializer.startTag(null, TAG_NAMESPACE_HASH); 1376 serializer.attribute(null, ATTR_NAMESPACE, namespace); 1377 serializer.attribute(null, ATTR_BANNED_HASH, bannedHashCode); 1378 serializer.endTag(null, TAG_NAMESPACE_HASH); 1379 return true; 1380 } 1381 1382 private static String hashCode(Map<String, String> keyValues) { 1383 return Integer.toString(keyValues.hashCode()); 1384 } 1385 1386 private String getValueAttribute(TypedXmlPullParser parser, String attr, String base64Attr) { 1387 if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) { 1388 final String value = parser.getAttributeValue(null, attr); 1389 if (value != null) { 1390 return value; 1391 } 1392 final String base64 = parser.getAttributeValue(null, base64Attr); 1393 if (base64 != null) { 1394 return base64Decode(base64); 1395 } 1396 // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64. 1397 return null; 1398 } else { 1399 // Old encoding. 1400 final String stored = parser.getAttributeValue(null, attr); 1401 if (NULL_VALUE_OLD_STYLE.equals(stored)) { 1402 return null; 1403 } else { 1404 return stored; 1405 } 1406 } 1407 } 1408 1409 @GuardedBy("mLock") 1410 private void readStateSyncLocked() throws IllegalStateException { 1411 FileInputStream in; 1412 AtomicFile file = new AtomicFile(mStatePersistFile); 1413 try { 1414 in = file.openRead(); 1415 } catch (FileNotFoundException fnfe) { 1416 Slog.w(LOG_TAG, "No settings state " + mStatePersistFile); 1417 if (DEBUG) { 1418 logSettingsDirectoryInformation(mStatePersistFile); 1419 } 1420 addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null); 1421 return; 1422 } 1423 if (parseStateFromXmlStreamLocked(in)) { 1424 return; 1425 } 1426 1427 // Settings file exists but is corrupted. Retry with the fallback file 1428 final File statePersistFallbackFile = new File( 1429 mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX); 1430 Slog.w(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile 1431 + ", retrying with fallback file: " + statePersistFallbackFile); 1432 try { 1433 in = new AtomicFile(statePersistFallbackFile).openRead(); 1434 } catch (FileNotFoundException fnfe) { 1435 final String message = "No fallback file found for: " + mStatePersistFile; 1436 Slog.wtf(LOG_TAG, message); 1437 if (!isConfigSettingsKey(mKey)) { 1438 // Allow partially deserialized config settings because they can be updated later 1439 throw new IllegalStateException(message); 1440 } 1441 } 1442 if (parseStateFromXmlStreamLocked(in)) { 1443 // Parsed state from fallback file. Restore original file with fallback file 1444 try { 1445 FileUtils.copy(statePersistFallbackFile, mStatePersistFile); 1446 } catch (IOException ignored) { 1447 // Failed to copy, but it's okay because we already parsed states from fallback file 1448 } 1449 } else { 1450 final String message = "Failed parsing settings file: " + mStatePersistFile; 1451 Slog.wtf(LOG_TAG, message); 1452 if (!isConfigSettingsKey(mKey)) { 1453 // Allow partially deserialized config settings because they can be updated later 1454 throw new IllegalStateException(message); 1455 } 1456 } 1457 } 1458 1459 @GuardedBy("mLock") 1460 private boolean parseStateFromXmlStreamLocked(FileInputStream in) { 1461 try { 1462 TypedXmlPullParser parser = Xml.resolvePullParser(in); 1463 parseStateLocked(parser); 1464 return true; 1465 } catch (XmlPullParserException | IOException | NumberFormatException e) { 1466 Slog.e(LOG_TAG, "parse settings xml failed", e); 1467 return false; 1468 } finally { 1469 IoUtils.closeQuietly(in); 1470 } 1471 } 1472 1473 /** 1474 * Uses AtomicFile to check if the file or its backup exists. 1475 * 1476 * @param file The file to check for existence 1477 * @return whether the original or backup exist 1478 */ 1479 public static boolean stateFileExists(File file) { 1480 AtomicFile stateFile = new AtomicFile(file); 1481 return stateFile.exists(); 1482 } 1483 1484 private void parseStateLocked(TypedXmlPullParser parser) 1485 throws IOException, XmlPullParserException, NumberFormatException { 1486 final int outerDepth = parser.getDepth(); 1487 int type; 1488 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1489 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1490 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1491 continue; 1492 } 1493 1494 String tagName = parser.getName(); 1495 if (tagName.equals(TAG_SETTINGS)) { 1496 parseSettingsLocked(parser); 1497 } else if (tagName.equals(TAG_NAMESPACE_HASHES)) { 1498 parseNamespaceHash(parser); 1499 } 1500 } 1501 } 1502 1503 /** 1504 * Transforms a staged flag name to its real flag name. 1505 * 1506 * Staged flags take the form {@code staged/namespace*flagName}. If 1507 * {@code stagedFlagName} takes the proper form, returns 1508 * {@code namespace/flagName}. Otherwise, returns {@code stagedFlagName} 1509 * unmodified, and logs an error message. 1510 * 1511 */ 1512 @VisibleForTesting 1513 public static String createRealFlagName(String stagedFlagName) { 1514 int slashIndex = stagedFlagName.indexOf("/"); 1515 if (slashIndex == -1 || slashIndex == stagedFlagName.length() - 1 1516 || slashIndex == 0) { 1517 Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); 1518 return stagedFlagName; 1519 } 1520 1521 String namespaceAndFlag = 1522 stagedFlagName.substring(slashIndex + 1); 1523 1524 int starIndex = namespaceAndFlag.indexOf("*"); 1525 if (starIndex == -1 || starIndex == namespaceAndFlag.length() - 1 1526 || starIndex == 0) { 1527 Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); 1528 return stagedFlagName; 1529 } 1530 1531 String namespace = 1532 namespaceAndFlag.substring(0, starIndex); 1533 String flagName = 1534 namespaceAndFlag.substring(starIndex + 1); 1535 1536 return namespace + "/" + flagName; 1537 } 1538 1539 @GuardedBy("mLock") 1540 private void parseSettingsLocked(TypedXmlPullParser parser) 1541 throws IOException, XmlPullParserException, NumberFormatException { 1542 1543 mVersion = parser.getAttributeInt(null, ATTR_VERSION); 1544 1545 final int outerDepth = parser.getDepth(); 1546 int type; 1547 HashSet<String> flagsWithStagedValueApplied = new HashSet<String>(); 1548 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1549 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1550 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1551 continue; 1552 } 1553 1554 String tagName = parser.getName(); 1555 if (tagName.equals(TAG_SETTING)) { 1556 String id = parser.getAttributeValue(null, ATTR_ID); 1557 String name = parser.getAttributeValue(null, ATTR_NAME); 1558 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64); 1559 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 1560 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE, 1561 ATTR_DEFAULT_VALUE_BASE64); 1562 boolean isPreservedInRestore = parser.getAttributeBoolean(null, 1563 ATTR_PRESERVE_IN_RESTORE, false); 1564 String tag = null; 1565 boolean fromSystem = false; 1566 if (defaultValue != null) { 1567 fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false); 1568 tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); 1569 } 1570 1571 if (isConfigSettingsKey(mKey)) { 1572 if (flagsWithStagedValueApplied.contains(name)) { 1573 continue; 1574 } 1575 1576 if (name.startsWith(CONFIG_STAGED_PREFIX)) { 1577 name = createRealFlagName(name); 1578 flagsWithStagedValueApplied.add(name); 1579 } 1580 } 1581 1582 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, 1583 fromSystem, Long.valueOf(id), isPreservedInRestore)); 1584 1585 if (DEBUG_PERSISTENCE) { 1586 Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value); 1587 } 1588 } 1589 } 1590 1591 if (isConfigSettingsKey(mKey) && !flagsWithStagedValueApplied.isEmpty()) { 1592 // On boot, the config table XML file includes special staged flags. On the initial 1593 // boot XML -> HashMap parse, these staged flags get transformed into real flags. 1594 // After this, the HashMap contains no special staged flags (only the transformed 1595 // real flags), but the XML still does. We then have no need for the special staged 1596 // flags in the XML, so we overwrite the XML with the latest contents of the 1597 // HashMap. 1598 writeStateAsyncLocked(); 1599 } 1600 } 1601 1602 @GuardedBy("mLock") 1603 private void parseNamespaceHash(TypedXmlPullParser parser) 1604 throws IOException, XmlPullParserException { 1605 1606 final int outerDepth = parser.getDepth(); 1607 int type; 1608 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1609 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1610 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1611 continue; 1612 } 1613 1614 if (parser.getName().equals(TAG_NAMESPACE_HASH)) { 1615 String namespace = parser.getAttributeValue(null, ATTR_NAMESPACE); 1616 String bannedHashCode = parser.getAttributeValue(null, ATTR_BANNED_HASH); 1617 mNamespaceBannedHashes.put(namespace, bannedHashCode); 1618 } 1619 } 1620 } 1621 1622 private static Map<String, String> removeNullValueOldStyle(Map<String, String> keyValues) { 1623 Iterator<Map.Entry<String, String>> it = keyValues.entrySet().iterator(); 1624 while (it.hasNext()) { 1625 Map.Entry<String, String> keyValueEntry = it.next(); 1626 if (NULL_VALUE_OLD_STYLE.equals(keyValueEntry.getValue())) { 1627 keyValueEntry.setValue(null); 1628 } 1629 } 1630 return keyValues; 1631 } 1632 1633 private final class MyHandler extends Handler { 1634 public static final int MSG_PERSIST_SETTINGS = 1; 1635 1636 public MyHandler(Looper looper) { 1637 super(looper); 1638 } 1639 1640 @Override 1641 public void handleMessage(Message message) { 1642 switch (message.what) { 1643 case MSG_PERSIST_SETTINGS: { 1644 Runnable callback = (Runnable) message.obj; 1645 doWriteState(); 1646 if (callback != null) { 1647 callback.run(); 1648 } 1649 } 1650 break; 1651 } 1652 } 1653 } 1654 1655 private class HistoricalOperation { 1656 final long mTimestamp; 1657 final String mOperation; 1658 final Setting mSetting; 1659 1660 public HistoricalOperation(long timestamp, 1661 String operation, Setting setting) { 1662 mTimestamp = timestamp; 1663 mOperation = operation; 1664 mSetting = setting; 1665 } 1666 } 1667 1668 public class Setting { 1669 private String name; 1670 private String value; 1671 private String defaultValue; 1672 private String packageName; 1673 private long id; 1674 private String tag; 1675 // Whether the default is set by the system 1676 private boolean defaultFromSystem; 1677 // Whether the value of this setting will be preserved when restore happens. 1678 private boolean isValuePreservedInRestore; 1679 1680 public Setting(Setting other) { 1681 name = other.name; 1682 value = other.value; 1683 defaultValue = other.defaultValue; 1684 packageName = other.packageName; 1685 id = other.id; 1686 defaultFromSystem = other.defaultFromSystem; 1687 tag = other.tag; 1688 isValuePreservedInRestore = other.isValuePreservedInRestore; 1689 } 1690 1691 public Setting(String name, String value, boolean makeDefault, String packageName, 1692 String tag) { 1693 this(name, value, makeDefault, packageName, tag, false); 1694 } 1695 1696 Setting(String name, String value, boolean makeDefault, String packageName, 1697 String tag, boolean forceNonSystemPackage) { 1698 this.name = name; 1699 // overrideableByRestore = true as the first initialization isn't considered a 1700 // modification. 1701 update(value, makeDefault, packageName, tag, forceNonSystemPackage, true); 1702 } 1703 1704 public Setting(String name, String value, String defaultValue, 1705 String packageName, String tag, boolean fromSystem, long id) { 1706 this(name, value, defaultValue, packageName, tag, fromSystem, id, 1707 /* isOverrideableByRestore */ false); 1708 } 1709 1710 Setting(String name, String value, String defaultValue, 1711 String packageName, String tag, boolean fromSystem, long id, 1712 boolean isValuePreservedInRestore) { 1713 mNextId = Math.max(mNextId, id + 1); 1714 init(name, value, tag, defaultValue, packageName, fromSystem, id, 1715 isValuePreservedInRestore); 1716 } 1717 1718 private void init(String name, String value, String tag, String defaultValue, 1719 String packageName, boolean fromSystem, long id, 1720 boolean isValuePreservedInRestore) { 1721 this.name = name; 1722 this.value = internValue(value); 1723 this.tag = tag; 1724 this.defaultValue = internValue(defaultValue); 1725 this.packageName = TextUtils.safeIntern(packageName); 1726 this.id = id; 1727 this.defaultFromSystem = fromSystem; 1728 this.isValuePreservedInRestore = isValuePreservedInRestore; 1729 } 1730 1731 public String getName() { 1732 return name; 1733 } 1734 1735 public int getKey() { 1736 return mKey; 1737 } 1738 1739 public String getValue() { 1740 return value; 1741 } 1742 1743 public String getTag() { 1744 return tag; 1745 } 1746 1747 public String getDefaultValue() { 1748 return defaultValue; 1749 } 1750 1751 public String getPackageName() { 1752 return packageName; 1753 } 1754 1755 public boolean isDefaultFromSystem() { 1756 return defaultFromSystem; 1757 } 1758 1759 public boolean isValuePreservedInRestore() { 1760 return isValuePreservedInRestore; 1761 } 1762 1763 public long getId() { 1764 return id; 1765 } 1766 1767 public boolean isNull() { 1768 return false; 1769 } 1770 1771 /** @return whether the value changed */ 1772 public boolean reset() { 1773 // overrideableByRestore = true as resetting to default value isn't considered a 1774 // modification. 1775 return update(this.defaultValue, false, packageName, null, true, true, 1776 /* resetToDefault */ true); 1777 } 1778 1779 public boolean isTransient() { 1780 switch (getTypeFromKey(getKey())) { 1781 case SETTINGS_TYPE_GLOBAL: 1782 return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName()); 1783 } 1784 return false; 1785 } 1786 1787 public boolean update(String value, boolean setDefault, String packageName, String tag, 1788 boolean forceNonSystemPackage, boolean overrideableByRestore) { 1789 return update(value, setDefault, packageName, tag, forceNonSystemPackage, 1790 overrideableByRestore, /* resetToDefault */ false); 1791 } 1792 1793 private boolean update(String value, boolean setDefault, String packageName, String tag, 1794 boolean forceNonSystemPackage, boolean overrideableByRestore, 1795 boolean resetToDefault) { 1796 final boolean callerSystem = !forceNonSystemPackage && 1797 !isNull() && (isCalledFromSystem(packageName) 1798 || isSystemPackage(mContext, packageName)); 1799 // Settings set by the system are always defaults. 1800 if (callerSystem) { 1801 setDefault = true; 1802 } 1803 1804 String defaultValue = this.defaultValue; 1805 boolean defaultFromSystem = this.defaultFromSystem; 1806 if (setDefault) { 1807 if (!Objects.equals(value, this.defaultValue) 1808 && (!defaultFromSystem || callerSystem)) { 1809 defaultValue = value; 1810 // Default null means no default, so the tag is irrelevant 1811 // since it is used to reset a settings subset their defaults. 1812 // Also it is irrelevant if the system set the canonical default. 1813 if (defaultValue == null) { 1814 tag = null; 1815 defaultFromSystem = false; 1816 } 1817 } 1818 if (!defaultFromSystem && value != null) { 1819 if (callerSystem) { 1820 defaultFromSystem = true; 1821 } 1822 } 1823 } 1824 1825 // isValuePreservedInRestore shouldn't change back to false if it has been set to true. 1826 boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault, 1827 packageName, value); 1828 1829 // Is something gonna change? 1830 if (Objects.equals(value, this.value) 1831 && Objects.equals(defaultValue, this.defaultValue) 1832 && Objects.equals(packageName, this.packageName) 1833 && Objects.equals(tag, this.tag) 1834 && defaultFromSystem == this.defaultFromSystem 1835 && isPreserved == this.isValuePreservedInRestore) { 1836 return false; 1837 } 1838 1839 init(name, value, tag, defaultValue, packageName, defaultFromSystem, 1840 mNextId++, isPreserved); 1841 1842 return true; 1843 } 1844 1845 public String toString() { 1846 return "Setting{name=" + name + " value=" + value 1847 + (defaultValue != null ? " default=" + defaultValue : "") 1848 + " packageName=" + packageName + " tag=" + tag 1849 + " defaultFromSystem=" + defaultFromSystem + "}"; 1850 } 1851 1852 /** 1853 * Interns a string if it's a common setting value. 1854 * Otherwise returns the given string. 1855 */ 1856 static String internValue(String str) { 1857 if (str == null) { 1858 return null; 1859 } 1860 switch (str) { 1861 case "true": 1862 return "true"; 1863 case "false": 1864 return "false"; 1865 case "0": 1866 return "0"; 1867 case "1": 1868 return "1"; 1869 case "": 1870 return ""; 1871 case "null": 1872 return null; // explicit null has special handling 1873 default: 1874 return str; 1875 } 1876 } 1877 1878 private boolean shouldPreserveSetting(boolean overrideableByRestore, 1879 boolean resetToDefault, String packageName, String value) { 1880 if (resetToDefault) { 1881 // By default settings are not marked as preserved. 1882 return false; 1883 } 1884 if (value != null && value.equals(this.value) 1885 && SYSTEM_PACKAGE_NAME.equals(packageName)) { 1886 // Do not mark preserved if it's the system reinitializing to the same value. 1887 return false; 1888 } 1889 1890 // isValuePreservedInRestore shouldn't change back to false if it has been set to true. 1891 return this.isValuePreservedInRestore || !overrideableByRestore; 1892 } 1893 } 1894 1895 /** 1896 * @return TRUE if a string is considered "binary" from KXML's point of view. NOTE DO NOT 1897 * pass null. 1898 */ 1899 public static boolean isBinary(String s) { 1900 if (s == null) { 1901 throw new NullPointerException(); 1902 } 1903 // See KXmlSerializer.writeEscaped 1904 for (int i = 0; i < s.length(); i++) { 1905 char c = s.charAt(i); 1906 boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 1907 if (!allowedInXml) { 1908 return true; 1909 } 1910 } 1911 return false; 1912 } 1913 1914 private static String base64Encode(String s) { 1915 return Base64.encodeToString(toBytes(s), Base64.NO_WRAP); 1916 } 1917 1918 private static String base64Decode(String s) { 1919 return fromBytes(Base64.decode(s, Base64.DEFAULT)); 1920 } 1921 1922 // Note the followings are basically just UTF-16 encode/decode. But we want to preserve 1923 // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves, 1924 // since I don't know how Charset would treat them. 1925 1926 private static byte[] toBytes(String s) { 1927 final byte[] result = new byte[s.length() * 2]; 1928 int resultIndex = 0; 1929 for (int i = 0; i < s.length(); ++i) { 1930 char ch = s.charAt(i); 1931 result[resultIndex++] = (byte) (ch >> 8); 1932 result[resultIndex++] = (byte) ch; 1933 } 1934 return result; 1935 } 1936 1937 private static String fromBytes(byte[] bytes) { 1938 final StringBuilder sb = new StringBuilder(bytes.length / 2); 1939 1940 final int last = bytes.length - 1; 1941 1942 for (int i = 0; i < last; i += 2) { 1943 final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff)); 1944 sb.append(ch); 1945 } 1946 return sb.toString(); 1947 } 1948 1949 // Cache the list of names of system packages. This is only called once on system boot. 1950 public static void cacheSystemPackageNamesAndSystemSignature(@NonNull Context context) { 1951 final PackageManager packageManager = context.getPackageManager(); 1952 final long identity = Binder.clearCallingIdentity(); 1953 try { 1954 sSystemPackages.add(SYSTEM_PACKAGE_NAME); 1955 // Cache SetupWizard package name. 1956 final String setupWizPackageName = packageManager.getSetupWizardPackageName(); 1957 if (setupWizPackageName != null) { 1958 sSystemPackages.add(setupWizPackageName); 1959 } 1960 final List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0); 1961 final int installedPackagesCount = packageInfos.size(); 1962 for (int i = 0; i < installedPackagesCount; i++) { 1963 if (shouldAddToSystemPackages(packageInfos.get(i))) { 1964 sSystemPackages.add(packageInfos.get(i).packageName); 1965 } 1966 } 1967 } finally { 1968 Binder.restoreCallingIdentity(identity); 1969 } 1970 } 1971 1972 private static boolean shouldAddToSystemPackages(@NonNull PackageInfo packageInfo) { 1973 // Shell and Root are not considered a part of the system 1974 if (isShellOrRoot(packageInfo.packageName)) { 1975 return false; 1976 } 1977 // Already added 1978 if (sSystemPackages.contains(packageInfo.packageName)) { 1979 return false; 1980 } 1981 return isSystemPackage(packageInfo.applicationInfo); 1982 } 1983 1984 private static boolean isShellOrRoot(@NonNull String packageName) { 1985 return (SHELL_PACKAGE_NAME.equals(packageName) 1986 || ROOT_PACKAGE_NAME.equals(packageName)); 1987 } 1988 1989 private static boolean isCalledFromSystem(@NonNull String packageName) { 1990 // Shell and Root are not considered a part of the system 1991 if (isShellOrRoot(packageName)) { 1992 return false; 1993 } 1994 final int callingUid = Binder.getCallingUid(); 1995 // Native services running as a special UID get a pass 1996 final int callingAppId = UserHandle.getAppId(callingUid); 1997 return (callingAppId < FIRST_APPLICATION_UID); 1998 } 1999 2000 public static boolean isSystemPackage(@NonNull Context context, @NonNull String packageName) { 2001 // Check shell or root before trying to retrieve ApplicationInfo to fail fast 2002 if (isShellOrRoot(packageName)) { 2003 return false; 2004 } 2005 // If it's a known system package or known to be platform signed 2006 if (sSystemPackages.contains(packageName)) { 2007 return true; 2008 } 2009 2010 ApplicationInfo aInfo = null; 2011 final long identity = Binder.clearCallingIdentity(); 2012 try { 2013 try { 2014 // Notice that this makes a call to package manager inside the lock 2015 aInfo = context.getPackageManager().getApplicationInfo(packageName, 0); 2016 } catch (PackageManager.NameNotFoundException ignored) { 2017 } 2018 } finally { 2019 Binder.restoreCallingIdentity(identity); 2020 } 2021 return isSystemPackage(aInfo); 2022 } 2023 2024 private static boolean isSystemPackage(@Nullable ApplicationInfo aInfo) { 2025 if (aInfo == null) { 2026 return false; 2027 } 2028 // If the system or a special system UID (like telephony), done. 2029 if (aInfo.uid < FIRST_APPLICATION_UID) { 2030 return true; 2031 } 2032 // If a persistent system app, done. 2033 if ((aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 2034 && (aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 2035 return true; 2036 } 2037 // Platform signed packages are considered to be from the system 2038 if (aInfo.isSignedWithPlatformKey()) { 2039 return true; 2040 } 2041 return false; 2042 } 2043 2044 @VisibleForTesting 2045 public int getMemoryUsage(String packageName) { 2046 synchronized (mLock) { 2047 return mPackageToMemoryUsage.getOrDefault(packageName, 0); 2048 } 2049 } 2050 2051 /** 2052 * Allow tests to wait for the handler to finish handling all the remaining messages 2053 */ 2054 @VisibleForTesting 2055 public void waitForHandler() { 2056 final CountDownLatch latch = new CountDownLatch(1); 2057 synchronized (mLock) { 2058 mHandler.post(latch::countDown); 2059 } 2060 try { 2061 latch.await(); 2062 } catch (InterruptedException e) { 2063 // ignored 2064 } 2065 } 2066 } 2067