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