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