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