1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.settings; 18 19 import static android.os.Process.FIRST_APPLICATION_UID; 20 import static android.os.Process.INVALID_UID; 21 22 import android.annotation.NonNull; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManagerInternal; 28 import android.content.pm.Signature; 29 import android.os.Binder; 30 import android.os.Build; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.SystemClock; 35 import android.os.UserHandle; 36 import android.provider.Settings; 37 import android.provider.Settings.Global; 38 import android.providers.settings.SettingsOperationProto; 39 import android.text.TextUtils; 40 import android.util.ArrayMap; 41 import android.util.AtomicFile; 42 import android.util.Base64; 43 import android.util.Slog; 44 import android.util.SparseIntArray; 45 import android.util.StatsLog; 46 import android.util.TimeUtils; 47 import android.util.Xml; 48 import android.util.proto.ProtoOutputStream; 49 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.util.ArrayUtils; 52 import com.android.server.LocalServices; 53 54 import libcore.io.IoUtils; 55 56 import org.xmlpull.v1.XmlPullParser; 57 import org.xmlpull.v1.XmlPullParserException; 58 import org.xmlpull.v1.XmlSerializer; 59 60 import java.io.File; 61 import java.io.FileInputStream; 62 import java.io.FileNotFoundException; 63 import java.io.FileOutputStream; 64 import java.io.IOException; 65 import java.io.PrintWriter; 66 import java.nio.charset.StandardCharsets; 67 import java.util.ArrayList; 68 import java.util.List; 69 import java.util.Objects; 70 71 /** 72 * This class contains the state for one type of settings. It is responsible 73 * for saving the state asynchronously to an XML file after a mutation and 74 * loading the from an XML file on construction. 75 * <p> 76 * This class uses the same lock as the settings provider to ensure that 77 * multiple changes made by the settings provider, e,g, upgrade, bulk insert, 78 * etc, are atomically persisted since the asynchronous persistence is using 79 * the same lock to grab the current state to write to disk. 80 * </p> 81 */ 82 final class SettingsState { 83 private static final boolean DEBUG = false; 84 private static final boolean DEBUG_PERSISTENCE = false; 85 86 private static final String LOG_TAG = "SettingsState"; 87 88 static final String SYSTEM_PACKAGE_NAME = "android"; 89 90 static final int SETTINGS_VERSION_NEW_ENCODING = 121; 91 92 private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; 93 private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000; 94 95 public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1; 96 public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 20000; 97 98 public static final int VERSION_UNDEFINED = -1; 99 100 private static final String TAG_SETTINGS = "settings"; 101 private static final String TAG_SETTING = "setting"; 102 private static final String ATTR_PACKAGE = "package"; 103 private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet"; 104 private static final String ATTR_TAG = "tag"; 105 private static final String ATTR_TAG_BASE64 = "tagBase64"; 106 107 private static final String ATTR_VERSION = "version"; 108 private static final String ATTR_ID = "id"; 109 private static final String ATTR_NAME = "name"; 110 111 /** 112 * Non-binary value will be written in this attributes. 113 */ 114 private static final String ATTR_VALUE = "value"; 115 private static final String ATTR_DEFAULT_VALUE = "defaultValue"; 116 117 /** 118 * KXmlSerializer won't like some characters. We encode such characters 119 * in base64 and store in this attribute. 120 * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64. 121 */ 122 private static final String ATTR_VALUE_BASE64 = "valueBase64"; 123 private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; 124 125 // This was used in version 120 and before. 126 private static final String NULL_VALUE_OLD_STYLE = "null"; 127 128 private static final int HISTORICAL_OPERATION_COUNT = 20; 129 private static final String HISTORICAL_OPERATION_UPDATE = "update"; 130 private static final String HISTORICAL_OPERATION_DELETE = "delete"; 131 private static final String HISTORICAL_OPERATION_PERSIST = "persist"; 132 private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize"; 133 private static final String HISTORICAL_OPERATION_RESET = "reset"; 134 135 private static final String SHELL_PACKAGE_NAME = "com.android.shell"; 136 private static final String ROOT_PACKAGE_NAME = "root"; 137 138 private static final String NULL_VALUE = "null"; 139 140 private static final Object sLock = new Object(); 141 142 @GuardedBy("sLock") 143 private static final SparseIntArray sSystemUids = new SparseIntArray(); 144 145 @GuardedBy("sLock") 146 private static Signature sSystemSignature; 147 148 private final Object mWriteLock = new Object(); 149 150 private final Object mLock; 151 152 private final Handler mHandler; 153 154 @GuardedBy("mLock") 155 private final Context mContext; 156 157 @GuardedBy("mLock") 158 private final ArrayMap<String, Setting> mSettings = new ArrayMap<>(); 159 160 @GuardedBy("mLock") 161 private final ArrayMap<String, Integer> mPackageToMemoryUsage; 162 163 @GuardedBy("mLock") 164 private final int mMaxBytesPerAppPackage; 165 166 @GuardedBy("mLock") 167 private final File mStatePersistFile; 168 169 @GuardedBy("mLock") 170 private final String mStatePersistTag; 171 172 private final Setting mNullSetting = new Setting(null, null, false, null, null) { 173 @Override 174 public boolean isNull() { 175 return true; 176 } 177 }; 178 179 @GuardedBy("mLock") 180 private final List<HistoricalOperation> mHistoricalOperations; 181 182 @GuardedBy("mLock") 183 public final int mKey; 184 185 @GuardedBy("mLock") 186 private int mVersion = VERSION_UNDEFINED; 187 188 @GuardedBy("mLock") 189 private long mLastNotWrittenMutationTimeMillis; 190 191 @GuardedBy("mLock") 192 private boolean mDirty; 193 194 @GuardedBy("mLock") 195 private boolean mWriteScheduled; 196 197 @GuardedBy("mLock") 198 private long mNextId; 199 200 @GuardedBy("mLock") 201 private int mNextHistoricalOpIdx; 202 203 public static final int SETTINGS_TYPE_GLOBAL = 0; 204 public static final int SETTINGS_TYPE_SYSTEM = 1; 205 public static final int SETTINGS_TYPE_SECURE = 2; 206 public static final int SETTINGS_TYPE_SSAID = 3; 207 public static final int SETTINGS_TYPE_CONFIG = 4; 208 209 public static final int SETTINGS_TYPE_MASK = 0xF0000000; 210 public static final int SETTINGS_TYPE_SHIFT = 28; 211 makeKey(int type, int userId)212 public static int makeKey(int type, int userId) { 213 return (type << SETTINGS_TYPE_SHIFT) | userId; 214 } 215 getTypeFromKey(int key)216 public static int getTypeFromKey(int key) { 217 return key >>> SETTINGS_TYPE_SHIFT; 218 } 219 getUserIdFromKey(int key)220 public static int getUserIdFromKey(int key) { 221 return key & ~SETTINGS_TYPE_MASK; 222 } 223 settingTypeToString(int type)224 public static String settingTypeToString(int type) { 225 switch (type) { 226 case SETTINGS_TYPE_CONFIG: { 227 return "SETTINGS_CONFIG"; 228 } 229 case SETTINGS_TYPE_GLOBAL: { 230 return "SETTINGS_GLOBAL"; 231 } 232 case SETTINGS_TYPE_SECURE: { 233 return "SETTINGS_SECURE"; 234 } 235 case SETTINGS_TYPE_SYSTEM: { 236 return "SETTINGS_SYSTEM"; 237 } 238 case SETTINGS_TYPE_SSAID: { 239 return "SETTINGS_SSAID"; 240 } 241 default: { 242 return "UNKNOWN"; 243 } 244 } 245 } 246 keyToString(int key)247 public static String keyToString(int key) { 248 return "Key[user=" + getUserIdFromKey(key) + ";type=" 249 + settingTypeToString(getTypeFromKey(key)) + "]"; 250 } 251 SettingsState(Context context, Object lock, File file, int key, int maxBytesPerAppPackage, Looper looper)252 public SettingsState(Context context, Object lock, File file, int key, 253 int maxBytesPerAppPackage, Looper looper) { 254 // It is important that we use the same lock as the settings provider 255 // to ensure multiple mutations on this state are atomicaly persisted 256 // as the async persistence should be blocked while we make changes. 257 mContext = context; 258 mLock = lock; 259 mStatePersistFile = file; 260 mStatePersistTag = "settings-" + getTypeFromKey(key) + "-" + getUserIdFromKey(key); 261 mKey = key; 262 mHandler = new MyHandler(looper); 263 if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) { 264 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 265 mPackageToMemoryUsage = new ArrayMap<>(); 266 } else { 267 mMaxBytesPerAppPackage = maxBytesPerAppPackage; 268 mPackageToMemoryUsage = null; 269 } 270 271 mHistoricalOperations = Build.IS_DEBUGGABLE 272 ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; 273 274 synchronized (mLock) { 275 readStateSyncLocked(); 276 } 277 } 278 279 // The settings provider must hold its lock when calling here. 280 @GuardedBy("mLock") getVersionLocked()281 public int getVersionLocked() { 282 return mVersion; 283 } 284 getNullSetting()285 public Setting getNullSetting() { 286 return mNullSetting; 287 } 288 289 // The settings provider must hold its lock when calling here. 290 @GuardedBy("mLock") setVersionLocked(int version)291 public void setVersionLocked(int version) { 292 if (version == mVersion) { 293 return; 294 } 295 mVersion = version; 296 297 scheduleWriteIfNeededLocked(); 298 } 299 300 // The settings provider must hold its lock when calling here. 301 @GuardedBy("mLock") removeSettingsForPackageLocked(String packageName)302 public void removeSettingsForPackageLocked(String packageName) { 303 boolean removedSomething = false; 304 305 final int settingCount = mSettings.size(); 306 for (int i = settingCount - 1; i >= 0; i--) { 307 String name = mSettings.keyAt(i); 308 // Settings defined by us are never dropped. 309 if (Settings.System.PUBLIC_SETTINGS.contains(name) 310 || Settings.System.PRIVATE_SETTINGS.contains(name)) { 311 continue; 312 } 313 Setting setting = mSettings.valueAt(i); 314 if (packageName.equals(setting.packageName)) { 315 mSettings.removeAt(i); 316 removedSomething = true; 317 } 318 } 319 320 if (removedSomething) { 321 scheduleWriteIfNeededLocked(); 322 } 323 } 324 325 // The settings provider must hold its lock when calling here. 326 @GuardedBy("mLock") getSettingNamesLocked()327 public List<String> getSettingNamesLocked() { 328 ArrayList<String> names = new ArrayList<>(); 329 final int settingsCount = mSettings.size(); 330 for (int i = 0; i < settingsCount; i++) { 331 String name = mSettings.keyAt(i); 332 names.add(name); 333 } 334 return names; 335 } 336 337 // The settings provider must hold its lock when calling here. 338 @GuardedBy("mLock") getSettingLocked(String name)339 public Setting getSettingLocked(String name) { 340 if (TextUtils.isEmpty(name)) { 341 return mNullSetting; 342 } 343 Setting setting = mSettings.get(name); 344 if (setting != null) { 345 return new Setting(setting); 346 } 347 return mNullSetting; 348 } 349 350 // The settings provider must hold its lock when calling here. updateSettingLocked(String name, String value, String tag, boolean makeValue, String packageName)351 public boolean updateSettingLocked(String name, String value, String tag, 352 boolean makeValue, String packageName) { 353 if (!hasSettingLocked(name)) { 354 return false; 355 } 356 357 return insertSettingLocked(name, value, tag, makeValue, packageName); 358 } 359 360 // The settings provider must hold its lock when calling here. 361 @GuardedBy("mLock") resetSettingDefaultValueLocked(String name)362 public void resetSettingDefaultValueLocked(String name) { 363 Setting oldSetting = getSettingLocked(name); 364 if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) { 365 String oldValue = oldSetting.getValue(); 366 String oldDefaultValue = oldSetting.getDefaultValue(); 367 Setting newSetting = new Setting(name, oldSetting.getValue(), null, 368 oldSetting.getPackageName(), oldSetting.getTag(), false, 369 oldSetting.getId()); 370 mSettings.put(name, newSetting); 371 updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, 372 newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); 373 scheduleWriteIfNeededLocked(); 374 } 375 } 376 377 // The settings provider must hold its lock when calling here. 378 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, String packageName)379 public boolean insertSettingLocked(String name, String value, String tag, 380 boolean makeDefault, String packageName) { 381 return insertSettingLocked(name, value, tag, makeDefault, false, packageName); 382 } 383 384 // The settings provider must hold its lock when calling here. 385 @GuardedBy("mLock") insertSettingLocked(String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName)386 public boolean insertSettingLocked(String name, String value, String tag, 387 boolean makeDefault, boolean forceNonSystemPackage, String packageName) { 388 if (TextUtils.isEmpty(name)) { 389 return false; 390 } 391 392 Setting oldState = mSettings.get(name); 393 String oldValue = (oldState != null) ? oldState.value : null; 394 String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; 395 Setting newState; 396 397 if (oldState != null) { 398 if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) { 399 return false; 400 } 401 newState = oldState; 402 } else { 403 newState = new Setting(name, value, makeDefault, packageName, tag); 404 mSettings.put(name, newState); 405 } 406 407 StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag, 408 makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED); 409 410 addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); 411 412 updateMemoryUsagePerPackageLocked(packageName, oldValue, value, 413 oldDefaultValue, newState.getDefaultValue()); 414 415 scheduleWriteIfNeededLocked(); 416 417 return true; 418 } 419 420 // The settings provider must hold its lock when calling here. persistSyncLocked()421 public void persistSyncLocked() { 422 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 423 doWriteState(); 424 } 425 426 // The settings provider must hold its lock when calling here. 427 @GuardedBy("mLock") deleteSettingLocked(String name)428 public boolean deleteSettingLocked(String name) { 429 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 430 return false; 431 } 432 433 Setting oldState = mSettings.remove(name); 434 435 StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", 436 oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), 437 StatsLog.SETTING_CHANGED__REASON__DELETED); 438 439 updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, 440 null, oldState.defaultValue, null); 441 442 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); 443 444 scheduleWriteIfNeededLocked(); 445 446 return true; 447 } 448 449 // The settings provider must hold its lock when calling here. 450 @GuardedBy("mLock") resetSettingLocked(String name)451 public boolean resetSettingLocked(String name) { 452 if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 453 return false; 454 } 455 456 Setting setting = mSettings.get(name); 457 458 Setting oldSetting = new Setting(setting); 459 String oldValue = setting.getValue(); 460 String oldDefaultValue = setting.getDefaultValue(); 461 462 if (!setting.reset()) { 463 return false; 464 } 465 466 String newValue = setting.getValue(); 467 String newDefaultValue = setting.getDefaultValue(); 468 469 updateMemoryUsagePerPackageLocked(setting.packageName, oldValue, 470 newValue, oldDefaultValue, newDefaultValue); 471 472 addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); 473 474 scheduleWriteIfNeededLocked(); 475 476 return true; 477 } 478 479 // The settings provider must hold its lock when calling here. 480 @GuardedBy("mLock") destroyLocked(Runnable callback)481 public void destroyLocked(Runnable callback) { 482 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 483 if (callback != null) { 484 if (mDirty) { 485 // Do it without a delay. 486 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS, 487 callback).sendToTarget(); 488 return; 489 } 490 callback.run(); 491 } 492 } 493 494 @GuardedBy("mLock") addHistoricalOperationLocked(String type, Setting setting)495 private void addHistoricalOperationLocked(String type, Setting setting) { 496 if (mHistoricalOperations == null) { 497 return; 498 } 499 HistoricalOperation operation = new HistoricalOperation( 500 SystemClock.elapsedRealtime(), type, 501 setting != null ? new Setting(setting) : null); 502 if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) { 503 mHistoricalOperations.add(operation); 504 } else { 505 mHistoricalOperations.set(mNextHistoricalOpIdx, operation); 506 } 507 mNextHistoricalOpIdx++; 508 if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) { 509 mNextHistoricalOpIdx = 0; 510 } 511 } 512 513 /** 514 * Dump historical operations as a proto buf. 515 * 516 * @param proto The proto buf stream to dump to 517 * @param fieldId The repeated field ID to use to save an operation to. 518 */ dumpHistoricalOperations(@onNull ProtoOutputStream proto, long fieldId)519 void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) { 520 synchronized (mLock) { 521 if (mHistoricalOperations == null) { 522 return; 523 } 524 525 final int operationCount = mHistoricalOperations.size(); 526 for (int i = 0; i < operationCount; i++) { 527 int index = mNextHistoricalOpIdx - 1 - i; 528 if (index < 0) { 529 index = operationCount + index; 530 } 531 HistoricalOperation operation = mHistoricalOperations.get(index); 532 533 final long token = proto.start(fieldId); 534 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp); 535 proto.write(SettingsOperationProto.OPERATION, operation.mOperation); 536 if (operation.mSetting != null) { 537 // Only add the name of the setting, since we don't know the historical package 538 // and values for it so they would be misleading to add here (all we could 539 // add is what the current data is). 540 proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName()); 541 } 542 proto.end(token); 543 } 544 } 545 } 546 dumpHistoricalOperations(PrintWriter pw)547 public void dumpHistoricalOperations(PrintWriter pw) { 548 synchronized (mLock) { 549 if (mHistoricalOperations == null) { 550 return; 551 } 552 pw.println("Historical operations"); 553 final int operationCount = mHistoricalOperations.size(); 554 for (int i = 0; i < operationCount; i++) { 555 int index = mNextHistoricalOpIdx - 1 - i; 556 if (index < 0) { 557 index = operationCount + index; 558 } 559 HistoricalOperation operation = mHistoricalOperations.get(index); 560 pw.print(TimeUtils.formatForLogging(operation.mTimestamp)); 561 pw.print(" "); 562 pw.print(operation.mOperation); 563 if (operation.mSetting != null) { 564 pw.print(" "); 565 // Only print the name of the setting, since we don't know the 566 // historical package and values for it so they would be misleading 567 // to print here (all we could print is what the current data is). 568 pw.print(operation.mSetting.getName()); 569 } 570 pw.println(); 571 } 572 pw.println(); 573 pw.println(); 574 } 575 } 576 577 @GuardedBy("mLock") updateMemoryUsagePerPackageLocked(String packageName, String oldValue, String newValue, String oldDefaultValue, String newDefaultValue)578 private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, 579 String newValue, String oldDefaultValue, String newDefaultValue) { 580 if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { 581 return; 582 } 583 584 if (SYSTEM_PACKAGE_NAME.equals(packageName)) { 585 return; 586 } 587 588 final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; 589 final int newValueSize = (newValue != null) ? newValue.length() : 0; 590 final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; 591 final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; 592 final int deltaSize = newValueSize + newDefaultValueSize 593 - oldValueSize - oldDefaultValueSize; 594 595 Integer currentSize = mPackageToMemoryUsage.get(packageName); 596 final int newSize = Math.max((currentSize != null) 597 ? currentSize + deltaSize : deltaSize, 0); 598 599 if (newSize > mMaxBytesPerAppPackage) { 600 throw new IllegalStateException("You are adding too many system settings. " 601 + "You should stop using system settings for app specific data" 602 + " package: " + packageName); 603 } 604 605 if (DEBUG) { 606 Slog.i(LOG_TAG, "Settings for package: " + packageName 607 + " size: " + newSize + " bytes."); 608 } 609 610 mPackageToMemoryUsage.put(packageName, newSize); 611 } 612 613 @GuardedBy("mLock") hasSettingLocked(String name)614 private boolean hasSettingLocked(String name) { 615 return mSettings.indexOfKey(name) >= 0; 616 } 617 618 @GuardedBy("mLock") scheduleWriteIfNeededLocked()619 private void scheduleWriteIfNeededLocked() { 620 // If dirty then we have a write already scheduled. 621 if (!mDirty) { 622 mDirty = true; 623 writeStateAsyncLocked(); 624 } 625 } 626 627 @GuardedBy("mLock") writeStateAsyncLocked()628 private void writeStateAsyncLocked() { 629 final long currentTimeMillis = SystemClock.uptimeMillis(); 630 631 if (mWriteScheduled) { 632 mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 633 634 // If enough time passed, write without holding off anymore. 635 final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis 636 - mLastNotWrittenMutationTimeMillis; 637 if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) { 638 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget(); 639 return; 640 } 641 642 // Hold off a bit more as settings are frequently changing. 643 final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis 644 + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0); 645 final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis); 646 647 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 648 mHandler.sendMessageDelayed(message, writeDelayMillis); 649 } else { 650 mLastNotWrittenMutationTimeMillis = currentTimeMillis; 651 Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 652 mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS); 653 mWriteScheduled = true; 654 } 655 } 656 doWriteState()657 private void doWriteState() { 658 boolean wroteState = false; 659 final int version; 660 final ArrayMap<String, Setting> settings; 661 662 synchronized (mLock) { 663 version = mVersion; 664 settings = new ArrayMap<>(mSettings); 665 mDirty = false; 666 mWriteScheduled = false; 667 } 668 669 synchronized (mWriteLock) { 670 if (DEBUG_PERSISTENCE) { 671 Slog.i(LOG_TAG, "[PERSIST START]"); 672 } 673 674 AtomicFile destination = new AtomicFile(mStatePersistFile, mStatePersistTag); 675 FileOutputStream out = null; 676 try { 677 out = destination.startWrite(); 678 679 XmlSerializer serializer = Xml.newSerializer(); 680 serializer.setOutput(out, StandardCharsets.UTF_8.name()); 681 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", 682 true); 683 serializer.startDocument(null, true); 684 serializer.startTag(null, TAG_SETTINGS); 685 serializer.attribute(null, ATTR_VERSION, String.valueOf(version)); 686 687 final int settingCount = settings.size(); 688 for (int i = 0; i < settingCount; i++) { 689 Setting setting = settings.valueAt(i); 690 691 if (setting.isTransient()) { 692 if (DEBUG_PERSISTENCE) { 693 Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName()); 694 } 695 continue; 696 } 697 698 writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(), 699 setting.getValue(), setting.getDefaultValue(), setting.getPackageName(), 700 setting.getTag(), setting.isDefaultFromSystem()); 701 702 if (DEBUG_PERSISTENCE) { 703 Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" 704 + setting.getValue()); 705 } 706 } 707 708 serializer.endTag(null, TAG_SETTINGS); 709 serializer.endDocument(); 710 destination.finishWrite(out); 711 712 wroteState = true; 713 714 if (DEBUG_PERSISTENCE) { 715 Slog.i(LOG_TAG, "[PERSIST END]"); 716 } 717 } catch (Throwable t) { 718 Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t); 719 destination.failWrite(out); 720 } finally { 721 IoUtils.closeQuietly(out); 722 } 723 } 724 725 if (wroteState) { 726 synchronized (mLock) { 727 addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null); 728 } 729 } 730 } 731 writeSingleSetting(int version, XmlSerializer serializer, String id, String name, String value, String defaultValue, String packageName, String tag, boolean defaultSysSet)732 static void writeSingleSetting(int version, XmlSerializer serializer, String id, 733 String name, String value, String defaultValue, String packageName, 734 String tag, boolean defaultSysSet) throws IOException { 735 if (id == null || isBinary(id) || name == null || isBinary(name) 736 || packageName == null || isBinary(packageName)) { 737 // This shouldn't happen. 738 return; 739 } 740 serializer.startTag(null, TAG_SETTING); 741 serializer.attribute(null, ATTR_ID, id); 742 serializer.attribute(null, ATTR_NAME, name); 743 setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64, 744 version, serializer, value); 745 serializer.attribute(null, ATTR_PACKAGE, packageName); 746 if (defaultValue != null) { 747 setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64, 748 version, serializer, defaultValue); 749 serializer.attribute(null, ATTR_DEFAULT_SYS_SET, Boolean.toString(defaultSysSet)); 750 setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64, 751 version, serializer, tag); 752 } 753 serializer.endTag(null, TAG_SETTING); 754 } 755 setValueAttribute(String attr, String attrBase64, int version, XmlSerializer serializer, String value)756 static void setValueAttribute(String attr, String attrBase64, int version, 757 XmlSerializer serializer, String value) throws IOException { 758 if (version >= SETTINGS_VERSION_NEW_ENCODING) { 759 if (value == null) { 760 // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64. 761 } else if (isBinary(value)) { 762 serializer.attribute(null, attrBase64, base64Encode(value)); 763 } else { 764 serializer.attribute(null, attr, value); 765 } 766 } else { 767 // Old encoding. 768 if (value == null) { 769 serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE); 770 } else { 771 serializer.attribute(null, attr, value); 772 } 773 } 774 } 775 getValueAttribute(XmlPullParser parser, String attr, String base64Attr)776 private String getValueAttribute(XmlPullParser parser, String attr, String base64Attr) { 777 if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) { 778 final String value = parser.getAttributeValue(null, attr); 779 if (value != null) { 780 return value; 781 } 782 final String base64 = parser.getAttributeValue(null, base64Attr); 783 if (base64 != null) { 784 return base64Decode(base64); 785 } 786 // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64. 787 return null; 788 } else { 789 // Old encoding. 790 final String stored = parser.getAttributeValue(null, attr); 791 if (NULL_VALUE_OLD_STYLE.equals(stored)) { 792 return null; 793 } else { 794 return stored; 795 } 796 } 797 } 798 799 @GuardedBy("mLock") readStateSyncLocked()800 private void readStateSyncLocked() { 801 FileInputStream in; 802 try { 803 in = new AtomicFile(mStatePersistFile).openRead(); 804 } catch (FileNotFoundException fnfe) { 805 Slog.i(LOG_TAG, "No settings state " + mStatePersistFile); 806 addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null); 807 return; 808 } 809 try { 810 XmlPullParser parser = Xml.newPullParser(); 811 parser.setInput(in, StandardCharsets.UTF_8.name()); 812 parseStateLocked(parser); 813 } catch (XmlPullParserException | IOException e) { 814 String message = "Failed parsing settings file: " + mStatePersistFile; 815 Slog.wtf(LOG_TAG, message); 816 throw new IllegalStateException(message, e); 817 } finally { 818 IoUtils.closeQuietly(in); 819 } 820 } 821 822 /** 823 * Uses AtomicFile to check if the file or its backup exists. 824 * @param file The file to check for existence 825 * @return whether the original or backup exist 826 */ stateFileExists(File file)827 public static boolean stateFileExists(File file) { 828 AtomicFile stateFile = new AtomicFile(file); 829 return stateFile.exists(); 830 } 831 parseStateLocked(XmlPullParser parser)832 private void parseStateLocked(XmlPullParser parser) 833 throws IOException, XmlPullParserException { 834 final int outerDepth = parser.getDepth(); 835 int type; 836 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 837 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 838 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 839 continue; 840 } 841 842 String tagName = parser.getName(); 843 if (tagName.equals(TAG_SETTINGS)) { 844 parseSettingsLocked(parser); 845 } 846 } 847 } 848 849 @GuardedBy("mLock") parseSettingsLocked(XmlPullParser parser)850 private void parseSettingsLocked(XmlPullParser parser) 851 throws IOException, XmlPullParserException { 852 853 mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); 854 855 final int outerDepth = parser.getDepth(); 856 int type; 857 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 858 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 859 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 860 continue; 861 } 862 863 String tagName = parser.getName(); 864 if (tagName.equals(TAG_SETTING)) { 865 String id = parser.getAttributeValue(null, ATTR_ID); 866 String name = parser.getAttributeValue(null, ATTR_NAME); 867 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64); 868 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 869 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE, 870 ATTR_DEFAULT_VALUE_BASE64); 871 String tag = null; 872 boolean fromSystem = false; 873 if (defaultValue != null) { 874 fromSystem = Boolean.parseBoolean(parser.getAttributeValue( 875 null, ATTR_DEFAULT_SYS_SET)); 876 tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); 877 } 878 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, 879 fromSystem, id)); 880 881 if (DEBUG_PERSISTENCE) { 882 Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value); 883 } 884 } 885 } 886 } 887 888 private final class MyHandler extends Handler { 889 public static final int MSG_PERSIST_SETTINGS = 1; 890 MyHandler(Looper looper)891 public MyHandler(Looper looper) { 892 super(looper); 893 } 894 895 @Override handleMessage(Message message)896 public void handleMessage(Message message) { 897 switch (message.what) { 898 case MSG_PERSIST_SETTINGS: { 899 Runnable callback = (Runnable) message.obj; 900 doWriteState(); 901 if (callback != null) { 902 callback.run(); 903 } 904 } 905 break; 906 } 907 } 908 } 909 910 private class HistoricalOperation { 911 final long mTimestamp; 912 final String mOperation; 913 final Setting mSetting; 914 HistoricalOperation(long timestamp, String operation, Setting setting)915 public HistoricalOperation(long timestamp, 916 String operation, Setting setting) { 917 mTimestamp = timestamp; 918 mOperation = operation; 919 mSetting = setting; 920 } 921 } 922 923 class Setting { 924 private String name; 925 private String value; 926 private String defaultValue; 927 private String packageName; 928 private String id; 929 private String tag; 930 // Whether the default is set by the system 931 private boolean defaultFromSystem; 932 Setting(Setting other)933 public Setting(Setting other) { 934 name = other.name; 935 value = other.value; 936 defaultValue = other.defaultValue; 937 packageName = other.packageName; 938 id = other.id; 939 defaultFromSystem = other.defaultFromSystem; 940 tag = other.tag; 941 } 942 Setting(String name, String value, boolean makeDefault, String packageName, String tag)943 public Setting(String name, String value, boolean makeDefault, String packageName, 944 String tag) { 945 this.name = name; 946 update(value, makeDefault, packageName, tag, false); 947 } 948 Setting(String name, String value, String defaultValue, String packageName, String tag, boolean fromSystem, String id)949 public Setting(String name, String value, String defaultValue, 950 String packageName, String tag, boolean fromSystem, String id) { 951 mNextId = Math.max(mNextId, Long.parseLong(id) + 1); 952 if (NULL_VALUE.equals(value)) { 953 value = null; 954 } 955 init(name, value, tag, defaultValue, packageName, fromSystem, id); 956 } 957 init(String name, String value, String tag, String defaultValue, String packageName, boolean fromSystem, String id)958 private void init(String name, String value, String tag, String defaultValue, 959 String packageName, boolean fromSystem, String id) { 960 this.name = name; 961 this.value = value; 962 this.tag = tag; 963 this.defaultValue = defaultValue; 964 this.packageName = packageName; 965 this.id = id; 966 this.defaultFromSystem = fromSystem; 967 } 968 getName()969 public String getName() { 970 return name; 971 } 972 getKey()973 public int getKey() { 974 return mKey; 975 } 976 getValue()977 public String getValue() { 978 return value; 979 } 980 getTag()981 public String getTag() { 982 return tag; 983 } 984 getDefaultValue()985 public String getDefaultValue() { 986 return defaultValue; 987 } 988 getPackageName()989 public String getPackageName() { 990 return packageName; 991 } 992 isDefaultFromSystem()993 public boolean isDefaultFromSystem() { 994 return defaultFromSystem; 995 } 996 getId()997 public String getId() { 998 return id; 999 } 1000 isNull()1001 public boolean isNull() { 1002 return false; 1003 } 1004 1005 /** @return whether the value changed */ reset()1006 public boolean reset() { 1007 return update(this.defaultValue, false, packageName, null, true); 1008 } 1009 isTransient()1010 public boolean isTransient() { 1011 switch (getTypeFromKey(getKey())) { 1012 case SETTINGS_TYPE_GLOBAL: 1013 return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName()); 1014 } 1015 return false; 1016 } 1017 update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage)1018 public boolean update(String value, boolean setDefault, String packageName, String tag, 1019 boolean forceNonSystemPackage) { 1020 if (NULL_VALUE.equals(value)) { 1021 value = null; 1022 } 1023 1024 final boolean callerSystem = !forceNonSystemPackage && 1025 !isNull() && isSystemPackage(mContext, packageName); 1026 // Settings set by the system are always defaults. 1027 if (callerSystem) { 1028 setDefault = true; 1029 } 1030 1031 String defaultValue = this.defaultValue; 1032 boolean defaultFromSystem = this.defaultFromSystem; 1033 if (setDefault) { 1034 if (!Objects.equals(value, this.defaultValue) 1035 && (!defaultFromSystem || callerSystem)) { 1036 defaultValue = value; 1037 // Default null means no default, so the tag is irrelevant 1038 // since it is used to reset a settings subset their defaults. 1039 // Also it is irrelevant if the system set the canonical default. 1040 if (defaultValue == null) { 1041 tag = null; 1042 defaultFromSystem = false; 1043 } 1044 } 1045 if (!defaultFromSystem && value != null) { 1046 if (callerSystem) { 1047 defaultFromSystem = true; 1048 } 1049 } 1050 } 1051 1052 // Is something gonna change? 1053 if (Objects.equals(value, this.value) 1054 && Objects.equals(defaultValue, this.defaultValue) 1055 && Objects.equals(packageName, this.packageName) 1056 && Objects.equals(tag, this.tag) 1057 && defaultFromSystem == this.defaultFromSystem) { 1058 return false; 1059 } 1060 1061 init(name, value, tag, defaultValue, packageName, defaultFromSystem, 1062 String.valueOf(mNextId++)); 1063 return true; 1064 } 1065 toString()1066 public String toString() { 1067 return "Setting{name=" + name + " value=" + value 1068 + (defaultValue != null ? " default=" + defaultValue : "") 1069 + " packageName=" + packageName + " tag=" + tag 1070 + " defaultFromSystem=" + defaultFromSystem + "}"; 1071 } 1072 } 1073 1074 /** 1075 * @return TRUE if a string is considered "binary" from KXML's point of view. NOTE DO NOT 1076 * pass null. 1077 */ isBinary(String s)1078 public static boolean isBinary(String s) { 1079 if (s == null) { 1080 throw new NullPointerException(); 1081 } 1082 // See KXmlSerializer.writeEscaped 1083 for (int i = 0; i < s.length(); i++) { 1084 char c = s.charAt(i); 1085 boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 1086 if (!allowedInXml) { 1087 return true; 1088 } 1089 } 1090 return false; 1091 } 1092 base64Encode(String s)1093 private static String base64Encode(String s) { 1094 return Base64.encodeToString(toBytes(s), Base64.NO_WRAP); 1095 } 1096 base64Decode(String s)1097 private static String base64Decode(String s) { 1098 return fromBytes(Base64.decode(s, Base64.DEFAULT)); 1099 } 1100 1101 // Note the followings are basically just UTF-16 encode/decode. But we want to preserve 1102 // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves, 1103 // since I don't know how Charset would treat them. 1104 toBytes(String s)1105 private static byte[] toBytes(String s) { 1106 final byte[] result = new byte[s.length() * 2]; 1107 int resultIndex = 0; 1108 for (int i = 0; i < s.length(); ++i) { 1109 char ch = s.charAt(i); 1110 result[resultIndex++] = (byte) (ch >> 8); 1111 result[resultIndex++] = (byte) ch; 1112 } 1113 return result; 1114 } 1115 fromBytes(byte[] bytes)1116 private static String fromBytes(byte[] bytes) { 1117 final StringBuffer sb = new StringBuffer(bytes.length / 2); 1118 1119 final int last = bytes.length - 1; 1120 1121 for (int i = 0; i < last; i += 2) { 1122 final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff)); 1123 sb.append(ch); 1124 } 1125 return sb.toString(); 1126 } 1127 1128 // Check if a specific package belonging to the caller is part of the system package. isSystemPackage(Context context, String packageName)1129 public static boolean isSystemPackage(Context context, String packageName) { 1130 final int callingUid = Binder.getCallingUid(); 1131 final int callingUserId = UserHandle.getUserId(callingUid); 1132 return isSystemPackage(context, packageName, callingUid, callingUserId); 1133 } 1134 1135 // Check if a specific package, uid, and user ID are part of the system package. isSystemPackage(Context context, String packageName, int uid, int userId)1136 public static boolean isSystemPackage(Context context, String packageName, int uid, 1137 int userId) { 1138 synchronized (sLock) { 1139 if (SYSTEM_PACKAGE_NAME.equals(packageName)) { 1140 return true; 1141 } 1142 1143 // Shell and Root are not considered a part of the system 1144 if (SHELL_PACKAGE_NAME.equals(packageName) 1145 || ROOT_PACKAGE_NAME.equals(packageName)) { 1146 return false; 1147 } 1148 1149 if (uid != INVALID_UID) { 1150 // Native services running as a special UID get a pass 1151 final int callingAppId = UserHandle.getAppId(uid); 1152 if (callingAppId < FIRST_APPLICATION_UID) { 1153 sSystemUids.put(callingAppId, callingAppId); 1154 return true; 1155 } 1156 } 1157 1158 final long identity = Binder.clearCallingIdentity(); 1159 try { 1160 try { 1161 uid = context.getPackageManager().getPackageUidAsUser(packageName, 0, userId); 1162 } catch (PackageManager.NameNotFoundException e) { 1163 return false; 1164 } 1165 1166 // If the system or a special system UID (like telephony), done. 1167 if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) { 1168 sSystemUids.put(uid, uid); 1169 return true; 1170 } 1171 1172 // If already known system component, done. 1173 if (sSystemUids.indexOfKey(uid) >= 0) { 1174 return true; 1175 } 1176 1177 // If SetupWizard, done. 1178 PackageManagerInternal packageManagerInternal = LocalServices.getService( 1179 PackageManagerInternal.class); 1180 if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) { 1181 sSystemUids.put(uid, uid); 1182 return true; 1183 } 1184 1185 // If a persistent system app, done. 1186 PackageInfo packageInfo; 1187 try { 1188 packageInfo = context.getPackageManager().getPackageInfoAsUser( 1189 packageName, PackageManager.GET_SIGNATURES, userId); 1190 if ((packageInfo.applicationInfo.flags 1191 & ApplicationInfo.FLAG_PERSISTENT) != 0 1192 && (packageInfo.applicationInfo.flags 1193 & ApplicationInfo.FLAG_SYSTEM) != 0) { 1194 sSystemUids.put(uid, uid); 1195 return true; 1196 } 1197 } catch (PackageManager.NameNotFoundException e) { 1198 return false; 1199 } 1200 1201 // Last check if system signed. 1202 if (sSystemSignature == null) { 1203 try { 1204 sSystemSignature = context.getPackageManager().getPackageInfoAsUser( 1205 SYSTEM_PACKAGE_NAME, PackageManager.GET_SIGNATURES, 1206 UserHandle.USER_SYSTEM).signatures[0]; 1207 } catch (PackageManager.NameNotFoundException e) { 1208 /* impossible */ 1209 return false; 1210 } 1211 } 1212 if (sSystemSignature.equals(packageInfo.signatures[0])) { 1213 sSystemUids.put(uid, uid); 1214 return true; 1215 } 1216 } finally { 1217 Binder.restoreCallingIdentity(identity); 1218 } 1219 1220 return false; 1221 } 1222 } 1223 } 1224