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