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