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