• 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 static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
22 
23 import android.aconfig.Aconfig.flag_permission;
24 import android.aconfig.Aconfig.flag_state;
25 import android.aconfig.Aconfig.parsed_flag;
26 import android.aconfig.Aconfig.parsed_flags;
27 import android.aconfigd.AconfigdFlagInfo;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.content.Context;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.os.Binder;
35 import android.os.Build;
36 import android.os.FileUtils;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.SystemClock;
41 import android.os.UserHandle;
42 import android.provider.Settings;
43 import android.provider.Settings.Global;
44 import android.providers.settings.SettingsOperationProto;
45 import android.text.TextUtils;
46 import android.util.ArrayMap;
47 import android.util.ArraySet;
48 import android.util.AtomicFile;
49 import android.util.Base64;
50 import android.util.Slog;
51 import android.util.TimeUtils;
52 import android.util.Xml;
53 import android.util.proto.ProtoOutputStream;
54 
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.internal.util.ArrayUtils;
58 import com.android.internal.util.FrameworkStatsLog;
59 import com.android.modules.utils.TypedXmlPullParser;
60 import com.android.modules.utils.TypedXmlSerializer;
61 
62 import libcore.io.IoUtils;
63 
64 import org.xmlpull.v1.XmlPullParser;
65 import org.xmlpull.v1.XmlPullParserException;
66 
67 import java.io.File;
68 import java.io.FileInputStream;
69 import java.io.FileNotFoundException;
70 import java.io.FileOutputStream;
71 import java.io.IOException;
72 import java.io.PrintWriter;
73 import java.nio.file.Files;
74 import java.nio.file.Path;
75 import java.util.ArrayList;
76 import java.util.HashMap;
77 import java.util.HashSet;
78 import java.util.Iterator;
79 import java.util.LinkedList;
80 import java.util.List;
81 import java.util.Map;
82 import java.util.Objects;
83 import java.util.Set;
84 import java.util.concurrent.CountDownLatch;
85 
86 /**
87  * This class contains the state for one type of settings. It is responsible
88  * for saving the state asynchronously to an XML file after a mutation and
89  * loading the from an XML file on construction.
90  * <p>
91  * This class uses the same lock as the settings provider to ensure that
92  * multiple changes made by the settings provider, e,g, upgrade, bulk insert,
93  * etc, are atomically persisted since the asynchronous persistence is using
94  * the same lock to grab the current state to write to disk.
95  * </p>
96  */
97 public class SettingsState {
98     private static final boolean DEBUG = false;
99     private static final boolean DEBUG_PERSISTENCE = false;
100 
101     private static final String LOG_TAG = "SettingsState";
102 
103     static final String SYSTEM_PACKAGE_NAME = "android";
104 
105     static final int SETTINGS_VERSION_NEW_ENCODING = 121;
106 
107     // LINT.IfChange
108     public static final int MAX_LENGTH_PER_STRING = 32768;
109     // LINT.ThenChange(/services/core/java/com/android/server/audio/AudioDeviceInventory.java:settings_max_length_per_string)
110     private static final long WRITE_SETTINGS_DELAY_MILLIS = 200;
111     private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000;
112 
113     public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1;
114     public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 40000;
115 
116     public static final int VERSION_UNDEFINED = -1;
117 
118     public static final String FALLBACK_FILE_SUFFIX = ".fallback";
119 
120     private static final String TAG_SETTINGS = "settings";
121     private static final String TAG_SETTING = "setting";
122     private static final String ATTR_PACKAGE = "package";
123     private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet";
124     private static final String ATTR_TAG = "tag";
125     private static final String ATTR_TAG_BASE64 = "tagBase64";
126 
127     private static final String ATTR_VERSION = "version";
128     private static final String ATTR_ID = "id";
129     private static final String ATTR_NAME = "name";
130 
131     private static final String TAG_NAMESPACE_HASHES = "namespaceHashes";
132     private static final String TAG_NAMESPACE_HASH = "namespaceHash";
133     private static final String ATTR_NAMESPACE = "namespace";
134     private static final String ATTR_BANNED_HASH = "bannedHash";
135 
136     private static final String ATTR_PRESERVE_IN_RESTORE = "preserve_in_restore";
137 
138     /**
139      * Non-binary value will be written in this attributes.
140      */
141     private static final String ATTR_VALUE = "value";
142     private static final String ATTR_DEFAULT_VALUE = "defaultValue";
143 
144     /**
145      * KXmlSerializer won't like some characters. We encode such characters
146      * in base64 and store in this attribute.
147      * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64.
148      */
149     private static final String ATTR_VALUE_BASE64 = "valueBase64";
150     private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64";
151 
152     /**
153      * In the config table, there are special flags of the form {@code staged/namespace*flagName}.
154      * On boot, when the XML file is initially parsed, these transform into
155      * {@code namespace/flagName}, and the special staged flags are deleted.
156      */
157     private static final String CONFIG_STAGED_PREFIX = "staged/";
158 
159     private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
160             "/system/etc/aconfig_flags.pb",
161             "/system_ext/etc/aconfig_flags.pb",
162             "/product/etc/aconfig_flags.pb",
163             "/vendor/etc/aconfig_flags.pb");
164 
165     private static final String APEX_DIR = "/apex";
166     private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
167 
168     /**
169      * This tag is applied to all aconfig default value-loaded flags.
170      */
171     private static final String BOOT_LOADED_DEFAULT_TAG = "BOOT_LOADED_DEFAULT";
172 
173     // This was used in version 120 and before.
174     private static final String NULL_VALUE_OLD_STYLE = "null";
175 
176     private static final int HISTORICAL_OPERATION_COUNT = 20;
177     private static final String HISTORICAL_OPERATION_UPDATE = "update";
178     private static final String HISTORICAL_OPERATION_DELETE = "delete";
179     private static final String HISTORICAL_OPERATION_PERSIST = "persist";
180     private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize";
181     private static final String HISTORICAL_OPERATION_RESET = "reset";
182 
183     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
184     private static final String ROOT_PACKAGE_NAME = "root";
185 
186     private static final String NULL_VALUE = "null";
187 
188     // TOBO(b/312444587): remove after Test Mission 2.
189     // Bulk sync names
190     private static final String BULK_SYNC_MARKER = "aconfigd_marker/bulk_synced";
191     private static final String BULK_SYNC_TRIGGER_COUNTER =
192         "core_experiments_team_internal/BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter";
193 
194     private static final ArraySet<String> sSystemPackages = new ArraySet<>();
195 
196     private final Object mWriteLock = new Object();
197 
198     private final Object mLock;
199 
200     private final Handler mHandler;
201 
202     @GuardedBy("mLock")
203     private final Context mContext;
204 
205     @GuardedBy("mLock")
206     private final ArrayMap<String, Setting> mSettings = new ArrayMap<>();
207 
208     @GuardedBy("mLock")
209     private final ArrayMap<String, String> mNamespaceBannedHashes = new ArrayMap<>();
210 
211     @GuardedBy("mLock")
212     private final ArrayMap<String, Integer> mPackageToMemoryUsage;
213 
214     @GuardedBy("mLock")
215     private final int mMaxBytesPerAppPackage;
216 
217     @GuardedBy("mLock")
218     private final File mStatePersistFile;
219 
220     @GuardedBy("mLock")
221     private final String mStatePersistTag;
222 
223     private final Setting mNullSetting = new Setting(null, null, false, null, null) {
224         @Override
225         public boolean isNull() {
226             return true;
227         }
228     };
229 
230     @GuardedBy("mLock")
231     private final List<HistoricalOperation> mHistoricalOperations;
232 
233     @GuardedBy("mLock")
234     public final int mKey;
235 
236     @GuardedBy("mLock")
237     private int mVersion = VERSION_UNDEFINED;
238 
239     @GuardedBy("mLock")
240     private long mLastNotWrittenMutationTimeMillis;
241 
242     @GuardedBy("mLock")
243     private boolean mDirty;
244 
245     @GuardedBy("mLock")
246     private boolean mWriteScheduled;
247 
248     @GuardedBy("mLock")
249     private long mNextId;
250 
251     @GuardedBy("mLock")
252     private int mNextHistoricalOpIdx;
253 
254     @GuardedBy("mLock")
255     @NonNull
256     private Map<String, Map<String, String>> mNamespaceDefaults;
257 
258     // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
259     @NonNull
260     private Map<String, AconfigdFlagInfo> mAconfigDefaultFlags;
261 
262     public static final int SETTINGS_TYPE_GLOBAL = 0;
263     public static final int SETTINGS_TYPE_SYSTEM = 1;
264     public static final int SETTINGS_TYPE_SECURE = 2;
265     public static final int SETTINGS_TYPE_SSAID = 3;
266     public static final int SETTINGS_TYPE_CONFIG = 4;
267 
268     public static final int SETTINGS_TYPE_MASK = 0xF0000000;
269     public static final int SETTINGS_TYPE_SHIFT = 28;
270 
makeKey(int type, int userId)271     public static int makeKey(int type, int userId) {
272         return (type << SETTINGS_TYPE_SHIFT) | userId;
273     }
274 
getTypeFromKey(int key)275     public static int getTypeFromKey(int key) {
276         return key >>> SETTINGS_TYPE_SHIFT;
277     }
278 
getUserIdFromKey(int key)279     public static int getUserIdFromKey(int key) {
280         return key & ~SETTINGS_TYPE_MASK;
281     }
282 
settingTypeToString(int type)283     public static String settingTypeToString(int type) {
284         switch (type) {
285             case SETTINGS_TYPE_CONFIG: {
286                 return "SETTINGS_CONFIG";
287             }
288             case SETTINGS_TYPE_GLOBAL: {
289                 return "SETTINGS_GLOBAL";
290             }
291             case SETTINGS_TYPE_SECURE: {
292                 return "SETTINGS_SECURE";
293             }
294             case SETTINGS_TYPE_SYSTEM: {
295                 return "SETTINGS_SYSTEM";
296             }
297             case SETTINGS_TYPE_SSAID: {
298                 return "SETTINGS_SSAID";
299             }
300             default: {
301                 return "UNKNOWN";
302             }
303         }
304     }
305 
isConfigSettingsKey(int key)306     public static boolean isConfigSettingsKey(int key) {
307         return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG;
308     }
309 
isGlobalSettingsKey(int key)310     public static boolean isGlobalSettingsKey(int key) {
311         return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
312     }
313 
isSystemSettingsKey(int key)314     public static boolean isSystemSettingsKey(int key) {
315         return getTypeFromKey(key) == SETTINGS_TYPE_SYSTEM;
316     }
317 
isSecureSettingsKey(int key)318     public static boolean isSecureSettingsKey(int key) {
319         return getTypeFromKey(key) == SETTINGS_TYPE_SECURE;
320     }
321 
isSsaidSettingsKey(int key)322     public static boolean isSsaidSettingsKey(int key) {
323         return getTypeFromKey(key) == SETTINGS_TYPE_SSAID;
324     }
325 
keyToString(int key)326     public static String keyToString(int key) {
327         return "Key[user=" + getUserIdFromKey(key) + ";type="
328                 + settingTypeToString(getTypeFromKey(key)) + "]";
329     }
330 
SettingsState( Context context, Object lock, File file, int key, int maxBytesPerAppPackage, Looper looper)331     public SettingsState(
332             Context context,
333             Object lock,
334             File file,
335             int key,
336             int maxBytesPerAppPackage,
337             Looper looper) {
338         // It is important that we use the same lock as the settings provider
339         // to ensure multiple mutations on this state are atomically persisted
340         // as the async persistence should be blocked while we make changes.
341         mContext = context;
342         mLock = lock;
343         mStatePersistFile = file;
344         mStatePersistTag = "settings-" + getTypeFromKey(key) + "-" + getUserIdFromKey(key);
345         mKey = key;
346         mHandler = new MyHandler(looper);
347         if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) {
348             mMaxBytesPerAppPackage = maxBytesPerAppPackage;
349             mPackageToMemoryUsage = new ArrayMap<>();
350         } else {
351             mMaxBytesPerAppPackage = maxBytesPerAppPackage;
352             mPackageToMemoryUsage = null;
353         }
354 
355         mHistoricalOperations =
356                 Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
357 
358         mNamespaceDefaults = new HashMap<>();
359         mAconfigDefaultFlags = new HashMap<>();
360 
361         ProtoOutputStream requests = null;
362 
363         synchronized (mLock) {
364             readStateSyncLocked();
365 
366             if (Flags.loadAconfigDefaults()) {
367                 if (isConfigSettingsKey(mKey)) {
368                     loadAconfigDefaultValuesLocked(sAconfigTextProtoFilesOnDevice);
369                 }
370             }
371 
372             if (Flags.loadApexAconfigProtobufs()) {
373                 if (isConfigSettingsKey(mKey)) {
374                     List<String> apexProtoPaths = listApexProtoPaths();
375                     loadAconfigDefaultValuesLocked(apexProtoPaths);
376                 }
377             }
378 
379             if (enableAconfigStorageDaemon()) {
380                 if (isConfigSettingsKey(mKey)) {
381                     getAllAconfigFlagsFromSettings(mAconfigDefaultFlags);
382                 }
383             }
384         }
385     }
386 
387     @GuardedBy("mLock")
getAllAconfigFlagsFromSettings( @onNull Map<String, AconfigdFlagInfo> flagInfoDefault)388     public int getAllAconfigFlagsFromSettings(
389             @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
390         Map<String, AconfigdFlagInfo> ret = new HashMap<>();
391         int numSettings = mSettings.size();
392         int num_requests = 0;
393         for (int i = 0; i < numSettings; i++) {
394             String name = mSettings.keyAt(i);
395             Setting setting = mSettings.valueAt(i);
396             AconfigdFlagInfo flag =
397                     getFlagOverrideToSync(name, setting.getValue(), flagInfoDefault);
398             if (flag == null) {
399                 continue;
400             }
401             if (flag.getIsReadWrite()) {
402                 ++num_requests;
403             }
404         }
405         Slog.i(LOG_TAG, num_requests + " flag override requests created");
406         return num_requests;
407     }
408 
409     // TODO(b/341764371): migrate aconfig flag push to GMS core
410     @VisibleForTesting
411     @GuardedBy("mLock")
412     @Nullable
getFlagOverrideToSync( String name, String value, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault)413     public AconfigdFlagInfo getFlagOverrideToSync(
414             String name, String value, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
415         int slashIdx = name.indexOf("/");
416         if (slashIdx <= 0 || slashIdx >= name.length() - 1) {
417             Slog.e(LOG_TAG, "invalid flag name " + name);
418             return null;
419         }
420 
421         String namespace = name.substring(0, slashIdx);
422         namespace = namespace.intern();  // Many configs have the same namespace.
423         String fullFlagName = name.substring(slashIdx + 1);
424         boolean isLocal = false;
425 
426         // get actual fully qualified flag name <package>.<flag>, note this is done
427         // after staged flag is applied, so no need to check staged flags
428         if (namespace.equals("device_config_overrides")) {
429             int colonIdx = fullFlagName.indexOf(":");
430             if (colonIdx == -1) {
431                 Slog.e(LOG_TAG, "invalid local override flag name " + name);
432                 return null;
433             }
434             namespace = fullFlagName.substring(0, colonIdx);
435             fullFlagName = fullFlagName.substring(colonIdx + 1);
436             isLocal = true;
437         }
438         // get package name and flag name
439         int dotIdx = fullFlagName.lastIndexOf(".");
440         if (dotIdx == -1) {
441             Slog.e(LOG_TAG, "invalid override flag name " + name);
442             return null;
443         }
444         AconfigdFlagInfo flag = flagInfoDefault.get(fullFlagName);
445         if (flag == null || !namespace.equals(flag.getNamespace())) {
446             return null;
447         }
448 
449         if (isLocal) {
450             flag.setLocalFlagValue(value);
451         } else {
452             flag.setServerFlagValue(value);
453         }
454         return flag;
455     }
456 
457     @GuardedBy("mLock")
loadAconfigDefaultValuesLocked(List<String> filePaths)458     private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
459         for (String fileName : filePaths) {
460             File f = new File(fileName);
461             if (f.isFile() && f.canRead()) {
462                 try (FileInputStream inputStream = new FileInputStream(fileName)) {
463                     loadAconfigDefaultValues(
464                             inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
465                 } catch (IOException e) {
466                     Slog.e(LOG_TAG, "failed to read protobuf", e);
467                 }
468             } else {
469                 Slog.d(LOG_TAG, "No protobuf file at " + fileName);
470             }
471         }
472     }
473 
listApexProtoPaths()474     private List<String> listApexProtoPaths() {
475         LinkedList<String> paths = new LinkedList();
476 
477         File apexDirectory = new File(APEX_DIR);
478         if (!apexDirectory.isDirectory()) {
479             return paths;
480         }
481 
482         File[] subdirs = apexDirectory.listFiles();
483         if (subdirs == null) {
484             return paths;
485         }
486 
487         for (File prefix : subdirs) {
488             // For each mainline modules, there are two directories, one <modulepackage>/,
489             // and one <modulepackage>@<versioncode>/. Just read the former.
490             if (prefix.getAbsolutePath().contains("@")) {
491                 continue;
492             }
493 
494             File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX);
495             if (!protoPath.exists()) {
496                 continue;
497             }
498 
499             paths.add(protoPath.getAbsolutePath());
500         }
501         return paths;
502     }
503 
504     @VisibleForTesting
505     @GuardedBy("mLock")
addAconfigDefaultValuesFromMap( @onNull Map<String, Map<String, String>> defaultMap)506     public void addAconfigDefaultValuesFromMap(
507             @NonNull Map<String, Map<String, String>> defaultMap) {
508         mNamespaceDefaults.putAll(defaultMap);
509     }
510 
511     @VisibleForTesting
512     @GuardedBy("mLock")
loadAconfigDefaultValues( byte[] fileContents, @NonNull Map<String, Map<String, String>> defaultMap, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault)513     public static void loadAconfigDefaultValues(
514             byte[] fileContents,
515             @NonNull Map<String, Map<String, String>> defaultMap,
516             @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
517         try {
518             parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents);
519             for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
520                 if (!defaultMap.containsKey(flag.getNamespace())) {
521                     Map<String, String> defaults = new HashMap<>();
522                     defaultMap.put(flag.getNamespace(), defaults);
523                 }
524                 String fullFlagName = flag.getPackage() + "." + flag.getName();
525                 String flagName = flag.getNamespace() + "/" + fullFlagName;
526                 String flagValue = flag.getState() == flag_state.ENABLED ? "true" : "false";
527                 boolean isReadWrite = flag.getPermission() == flag_permission.READ_WRITE;
528                 defaultMap.get(flag.getNamespace()).put(flagName, flagValue);
529                 if (!flagInfoDefault.containsKey(fullFlagName)) {
530                     flagInfoDefault.put(
531                             fullFlagName,
532                             AconfigdFlagInfo.newBuilder()
533                                     .setPackageName(flag.getPackage())
534                                     .setFlagName(flag.getName())
535                                     .setDefaultFlagValue(flagValue)
536                                     .setIsReadWrite(isReadWrite)
537                                     .setNamespace(flag.getNamespace())
538                                     .build());
539                 }
540             }
541         } catch (IOException e) {
542             Slog.e(LOG_TAG, "failed to parse protobuf", e);
543         }
544     }
545 
546     // The settings provider must hold its lock when calling here.
547     @GuardedBy("mLock")
getVersionLocked()548     public int getVersionLocked() {
549         return mVersion;
550     }
551 
getNullSetting()552     public Setting getNullSetting() {
553         return mNullSetting;
554     }
555 
556     // The settings provider must hold its lock when calling here.
557     @GuardedBy("mLock")
setVersionLocked(int version)558     public void setVersionLocked(int version) {
559         if (version == mVersion) {
560             return;
561         }
562         mVersion = version;
563 
564         scheduleWriteIfNeededLocked();
565     }
566 
567     // The settings provider must hold its lock when calling here.
568     @GuardedBy("mLock")
removeSettingsForPackageLocked(String packageName)569     public void removeSettingsForPackageLocked(String packageName) {
570         final int settingCount = mSettings.size();
571         for (int i = settingCount - 1; i >= 0; i--) {
572             String name = mSettings.keyAt(i);
573             // Settings defined by us are never dropped.
574             if (Settings.System.PUBLIC_SETTINGS.contains(name)
575                     || Settings.System.PRIVATE_SETTINGS.contains(name)) {
576                 continue;
577             }
578             Setting setting = mSettings.valueAt(i);
579             if (packageName.equals(setting.packageName)) {
580                 deleteSettingLocked(setting.name);
581             }
582         }
583     }
584 
585     // The settings provider must hold its lock when calling here.
586     @GuardedBy("mLock")
getSettingNamesLocked()587     public List<String> getSettingNamesLocked() {
588         ArrayList<String> names = new ArrayList<>();
589         final int settingsCount = mSettings.size();
590         for (int i = 0; i < settingsCount; i++) {
591             String name = mSettings.keyAt(i);
592             names.add(name);
593         }
594         return names;
595     }
596 
597     @NonNull
getAconfigDefaultValues()598     public Map<String, Map<String, String>> getAconfigDefaultValues() {
599         synchronized (mLock) {
600             return mNamespaceDefaults;
601         }
602     }
603 
604     @NonNull
getAconfigDefaultFlags()605     public Map<String, AconfigdFlagInfo> getAconfigDefaultFlags() {
606         synchronized (mLock) {
607             return mAconfigDefaultFlags;
608         }
609     }
610 
611     // The settings provider must hold its lock when calling here.
getSettingLocked(String name)612     public Setting getSettingLocked(String name) {
613         if (TextUtils.isEmpty(name)) {
614             return mNullSetting;
615         }
616         Setting setting = mSettings.get(name);
617         if (setting != null) {
618             return new Setting(setting);
619         }
620         return mNullSetting;
621     }
622 
623     // The settings provider must hold its lock when calling here.
updateSettingLocked(String name, String value, String tag, boolean makeValue, String packageName)624     public boolean updateSettingLocked(String name, String value, String tag,
625             boolean makeValue, String packageName) {
626         if (!hasSettingLocked(name)) {
627             return false;
628         }
629 
630         return insertSettingLocked(name, value, tag, makeValue, packageName);
631     }
632 
633     // The settings provider must hold its lock when calling here.
634     @GuardedBy("mLock")
resetSettingDefaultValueLocked(String name)635     public void resetSettingDefaultValueLocked(String name) {
636         Setting oldSetting = getSettingLocked(name);
637         if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) {
638             String oldValue = oldSetting.getValue();
639             String oldDefaultValue = oldSetting.getDefaultValue();
640             Setting newSetting = new Setting(name, oldSetting.getValue(), null,
641                     oldSetting.getPackageName(), oldSetting.getTag(), false,
642                     oldSetting.getId());
643             int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0,
644                     oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
645             checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
646             mSettings.put(name, newSetting);
647             updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
648             scheduleWriteIfNeededLocked();
649         }
650     }
651 
652     // The settings provider must hold its lock when calling here.
insertSettingOverrideableByRestoreLocked(String name, String value, String tag, boolean makeDefault, String packageName)653     public boolean insertSettingOverrideableByRestoreLocked(String name, String value, String tag,
654             boolean makeDefault, String packageName) {
655         return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
656                 /* overrideableByRestore */ true);
657     }
658 
659     // The settings provider must hold its lock when calling here.
660     @GuardedBy("mLock")
insertSettingLocked(String name, String value, String tag, boolean makeDefault, String packageName)661     public boolean insertSettingLocked(String name, String value, String tag,
662             boolean makeDefault, String packageName) {
663         return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
664                 /* overrideableByRestore */ false);
665     }
666 
667     // The settings provider must hold its lock when calling here.
668     @GuardedBy("mLock")
insertSettingLocked(String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName, boolean overrideableByRestore)669     public boolean insertSettingLocked(String name, String value, String tag,
670             boolean makeDefault, boolean forceNonSystemPackage, String packageName,
671             boolean overrideableByRestore) {
672         if (TextUtils.isEmpty(name)) {
673             return false;
674         }
675 
676         // Aconfig flags are always boot stable, so we anytime we write one, we stage it to be
677         // applied on reboot.
678         if (Flags.stageAllAconfigFlags()) {
679             int slashIndex = name.indexOf("/");
680             boolean stageFlag = isConfigSettingsKey(mKey)
681                     && slashIndex != -1
682                     && slashIndex != 0
683                     && slashIndex != name.length();
684 
685             if (stageFlag) {
686                 String namespace = name.substring(0, slashIndex);
687                 String flag = name.substring(slashIndex + 1);
688 
689                 boolean isAconfig = mNamespaceDefaults.containsKey(namespace)
690                         && mNamespaceDefaults.get(namespace).containsKey(name);
691 
692                 if (isAconfig) {
693                     name = "staged/" + namespace + "*" + flag;
694                 }
695             }
696         }
697 
698         final boolean isNameTooLong = name.length() > SettingsState.MAX_LENGTH_PER_STRING;
699         final boolean isValueTooLong =
700                 value != null && value.length() > SettingsState.MAX_LENGTH_PER_STRING;
701         if (isNameTooLong || isValueTooLong) {
702             // only print the first few bytes of the name in case it is long
703             final String errorMessage = "The " + (isNameTooLong ? "name" : "value")
704                     + " of your setting ["
705                     + (name.length() > 20 ? (name.substring(0, 20) + "...") : name)
706                     + "] is too long. The max length allowed for the string is "
707                     + MAX_LENGTH_PER_STRING + ".";
708             throw new IllegalArgumentException(errorMessage);
709         }
710 
711         Setting oldState = mSettings.get(name);
712         String previousOwningPackage = (oldState != null) ? oldState.packageName : null;
713         // If the old state doesn't exist, no need to handle the owning package change
714         final boolean owningPackageChanged = previousOwningPackage != null
715                 && !previousOwningPackage.equals(packageName);
716 
717         String oldValue = (oldState != null) ? oldState.value : null;
718         String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
719         String newDefaultValue = makeDefault ? value : oldDefaultValue;
720 
721         int newSizeForCurrentPackage = getNewMemoryUsagePerPackageLocked(packageName,
722                 /* deltaKeyLength= */ (oldState == null || owningPackageChanged) ? name.length() : 0,
723                 /* oldValue= */ owningPackageChanged ? null : oldValue,
724                 /* newValue= */ value,
725                 /* oldDefaultValue= */ owningPackageChanged ? null : oldDefaultValue,
726                 /* newDefaultValue = */ newDefaultValue);
727         // Only check the memory usage for the current package. Even if the owning package
728         // has changed, the previous owning package will only have a reduced memory usage, so
729         // there is no need to check its memory usage.
730         checkNewMemoryUsagePerPackageLocked(packageName, newSizeForCurrentPackage);
731 
732         Setting newState;
733 
734         if (oldState != null) {
735             if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage,
736                     overrideableByRestore)) {
737                 return false;
738             }
739             newState = oldState;
740         } else {
741             newState = new Setting(name, value, makeDefault, packageName, tag,
742                     forceNonSystemPackage);
743             mSettings.put(name, newState);
744         }
745 
746         FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, value, newState.value,
747                 oldValue, tag, makeDefault, getUserIdFromKey(mKey),
748                 FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);
749 
750         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
751 
752         updateMemoryUsagePerPackageLocked(packageName, newSizeForCurrentPackage);
753 
754         if (owningPackageChanged) {
755             int newSizeForPreviousPackage = getNewMemoryUsagePerPackageLocked(previousOwningPackage,
756                     /* deltaKeyLength= */ -name.length(),
757                     /* oldValue= */ oldValue,
758                     /* newValue= */ null,
759                     /* oldDefaultValue= */ oldDefaultValue,
760                     /* newDefaultValue = */ null);
761             updateMemoryUsagePerPackageLocked(previousOwningPackage, newSizeForPreviousPackage);
762         }
763 
764         scheduleWriteIfNeededLocked();
765 
766         return true;
767     }
768 
769     @GuardedBy("mLock")
isNewConfigBannedLocked(String prefix, Map<String, String> keyValues)770     public boolean isNewConfigBannedLocked(String prefix, Map<String, String> keyValues) {
771         // Replaces old style "null" String values with actual null's. This is done to simulate
772         // what will happen to String "null" values when they are written to Settings. This needs to
773         // be done here make sure that config hash computed during is banned check matches the
774         // one computed during banning when values are already stored.
775         keyValues = removeNullValueOldStyle(keyValues);
776         String bannedHash = mNamespaceBannedHashes.get(prefix);
777         if (bannedHash == null) {
778             return false;
779         }
780         return bannedHash.equals(hashCode(keyValues));
781     }
782 
783     @GuardedBy("mLock")
unbanAllConfigIfBannedConfigUpdatedLocked(String prefix)784     public void unbanAllConfigIfBannedConfigUpdatedLocked(String prefix) {
785         // If the prefix updated is a banned namespace, clear mNamespaceBannedHashes
786         // to unban all unbanned namespaces.
787         if (mNamespaceBannedHashes.get(prefix) != null) {
788             mNamespaceBannedHashes.clear();
789             scheduleWriteIfNeededLocked();
790         }
791     }
792 
793     @GuardedBy("mLock")
banConfigurationLocked(String prefix, Map<String, String> keyValues)794     public void banConfigurationLocked(String prefix, Map<String, String> keyValues) {
795         if (prefix == null || keyValues.isEmpty()) {
796             return;
797         }
798         // The write is intentionally not scheduled here, banned hashes should and will be written
799         // when the related setting changes are written
800         mNamespaceBannedHashes.put(prefix, hashCode(keyValues));
801     }
802 
803     @GuardedBy("mLock")
getAllConfigPrefixesLocked()804     public Set<String> getAllConfigPrefixesLocked() {
805         Set<String> prefixSet = new HashSet<>();
806         final int settingsCount = mSettings.size();
807         for (int i = 0; i < settingsCount; i++) {
808             String name = mSettings.keyAt(i);
809             prefixSet.add(name.split("/")[0] + "/");
810         }
811         return prefixSet;
812     }
813 
814     // The settings provider must hold its lock when calling here.
815     // Returns the list of keys which changed (added, updated, or deleted).
816     @GuardedBy("mLock")
setSettingsLocked(String prefix, Map<String, String> keyValues, String packageName)817     public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues,
818             String packageName) {
819         List<String> changedKeys = new ArrayList<>();
820         final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator();
821         int index = prefix.lastIndexOf('/');
822         String namespace = index < 0 ? "" : prefix.substring(0, index);
823         Map<String, String> trunkFlagMap = (mNamespaceDefaults == null)
824                 ? null : mNamespaceDefaults.get(namespace);
825         // Delete old keys with the prefix that are not part of the new set.
826         // trunk flags will not be configured with restricted propagation
827         // trunk flags will be explicitly set, so not removing them here
828         while (iterator.hasNext()) {
829             Map.Entry<String, Setting> entry = iterator.next();
830             final String key = entry.getKey();
831             final Setting oldState = entry.getValue();
832             if (key != null && (trunkFlagMap == null || !trunkFlagMap.containsKey(key))
833                     && key.startsWith(prefix) && !keyValues.containsKey(key)) {
834                 iterator.remove();
835 
836                 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key,
837                         /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false,
838                         getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
839                 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
840                 changedKeys.add(key); // key was removed
841             }
842         }
843 
844         // Update/add new keys
845         for (String key : keyValues.keySet()) {
846             String value = keyValues.get(key);
847 
848             // Rename key if it's an aconfig flag.
849             String flagName = key;
850             if (Flags.stageAllAconfigFlags() && isConfigSettingsKey(mKey)) {
851                 int slashIndex = flagName.indexOf("/");
852                 boolean stageFlag = slashIndex > 0 && slashIndex != flagName.length();
853                 boolean isAconfig = trunkFlagMap != null && trunkFlagMap.containsKey(flagName);
854                 if (stageFlag && isAconfig) {
855                     String flagWithoutNamespace = flagName.substring(slashIndex + 1);
856                     flagName = "staged/" + namespace + "*" + flagWithoutNamespace;
857                 }
858             }
859 
860             String oldValue = null;
861             Setting state = mSettings.get(flagName);
862             if (state == null) {
863                 state = new Setting(flagName, value, false, packageName, null);
864                 mSettings.put(flagName, state);
865                 changedKeys.add(flagName); // key was added
866             } else if (state.value != value) {
867                 oldValue = state.value;
868                 state.update(value, false, packageName, null, true,
869                         /* overrideableByRestore */ false);
870                 changedKeys.add(flagName); // key was updated
871             } else {
872                 // this key/value already exists, no change and no logging necessary
873                 continue;
874             }
875 
876             FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, flagName, value, state.value,
877                     oldValue, /* tag */ null, /* make default */ false,
878                     getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);
879             addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, state);
880         }
881 
882         if (!changedKeys.isEmpty()) {
883             scheduleWriteIfNeededLocked();
884         }
885 
886         return changedKeys;
887     }
888 
889     // The settings provider must hold its lock when calling here.
890     public void persistSettingsLocked() {
891         mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
892         // schedule a write operation right away
893         mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget();
894     }
895 
896     // The settings provider must hold its lock when calling here.
897     @GuardedBy("mLock")
898     public boolean deleteSettingLocked(String name) {
899         if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
900             return false;
901         }
902 
903         Setting oldState = mSettings.remove(name);
904         if (oldState == null) {
905             return false;
906         }
907         int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName,
908                 -name.length() /* deltaKeySize */,
909                 oldState.value, null, oldState.defaultValue, null);
910 
911         FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
912                 /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
913                 FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
914 
915         updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
916 
917         addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
918 
919         scheduleWriteIfNeededLocked();
920 
921         return true;
922     }
923 
924     // The settings provider must hold its lock when calling here.
925     @GuardedBy("mLock")
926     public boolean resetSettingLocked(String name) {
927         if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
928             return false;
929         }
930 
931         Setting setting = mSettings.get(name);
932         if (setting == null) {
933             return false;
934         }
935 
936         Setting oldSetting = new Setting(setting);
937         String oldValue = setting.getValue();
938         String oldDefaultValue = setting.getDefaultValue();
939 
940         int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue,
941                 oldDefaultValue, oldDefaultValue, oldDefaultValue);
942         checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
943 
944         if (!setting.reset()) {
945             return false;
946         }
947 
948         updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
949 
950         addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
951 
952         scheduleWriteIfNeededLocked();
953 
954         return true;
955     }
956 
957     // The settings provider must hold its lock when calling here.
958     @GuardedBy("mLock")
959     public void destroyLocked(Runnable callback) {
960         mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
961         if (callback != null) {
962             if (mDirty) {
963                 // Do it without a delay.
964                 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS,
965                         callback).sendToTarget();
966                 return;
967             }
968             callback.run();
969         }
970     }
971 
972     @GuardedBy("mLock")
973     private void addHistoricalOperationLocked(String type, Setting setting) {
974         if (mHistoricalOperations == null) {
975             return;
976         }
977         HistoricalOperation operation = new HistoricalOperation(
978                 System.currentTimeMillis(), type,
979                 setting != null ? new Setting(setting) : null);
980         if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) {
981             mHistoricalOperations.add(operation);
982         } else {
983             mHistoricalOperations.set(mNextHistoricalOpIdx, operation);
984         }
985         mNextHistoricalOpIdx++;
986         if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) {
987             mNextHistoricalOpIdx = 0;
988         }
989     }
990 
991     /**
992      * Dump historical operations as a proto buf.
993      *
994      * @param proto   The proto buf stream to dump to
995      * @param fieldId The repeated field ID to use to save an operation to.
996      */
997     void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) {
998         synchronized (mLock) {
999             if (mHistoricalOperations == null) {
1000                 return;
1001             }
1002 
1003             final int operationCount = mHistoricalOperations.size();
1004             for (int i = 0; i < operationCount; i++) {
1005                 int index = mNextHistoricalOpIdx - 1 - i;
1006                 if (index < 0) {
1007                     index = operationCount + index;
1008                 }
1009                 HistoricalOperation operation = mHistoricalOperations.get(index);
1010 
1011                 final long token = proto.start(fieldId);
1012                 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp);
1013                 proto.write(SettingsOperationProto.OPERATION, operation.mOperation);
1014                 if (operation.mSetting != null) {
1015                     // Only add the name of the setting, since we don't know the historical package
1016                     // and values for it so they would be misleading to add here (all we could
1017                     // add is what the current data is).
1018                     proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName());
1019                 }
1020                 proto.end(token);
1021             }
1022         }
1023     }
1024 
1025     public void dumpHistoricalOperations(PrintWriter pw) {
1026         synchronized (mLock) {
1027             if (mHistoricalOperations == null) {
1028                 return;
1029             }
1030             pw.println("Historical operations");
1031             final int operationCount = mHistoricalOperations.size();
1032             for (int i = 0; i < operationCount; i++) {
1033                 int index = mNextHistoricalOpIdx - 1 - i;
1034                 if (index < 0) {
1035                     index = operationCount + index;
1036                 }
1037                 HistoricalOperation operation = mHistoricalOperations.get(index);
1038                 pw.print(TimeUtils.formatForLogging(operation.mTimestamp));
1039                 pw.print(" ");
1040                 pw.print(operation.mOperation);
1041                 if (operation.mSetting != null) {
1042                     pw.print(" ");
1043                     // Only print the name of the setting, since we don't know the
1044                     // historical package and values for it so they would be misleading
1045                     // to print here (all we could print is what the current data is).
1046                     pw.print(operation.mSetting.getName());
1047                 }
1048                 pw.println();
1049             }
1050             pw.println();
1051             pw.println();
1052         }
1053     }
1054 
1055     @GuardedBy("mLock")
1056     private boolean isExemptFromMemoryUsageCap(String packageName) {
1057         return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
1058                 || SYSTEM_PACKAGE_NAME.equals(packageName);
1059     }
1060 
1061     @GuardedBy("mLock")
1062     private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
1063             throws IllegalStateException {
1064         if (isExemptFromMemoryUsageCap(packageName)) {
1065             return;
1066         }
1067         if (newSize > mMaxBytesPerAppPackage) {
1068             throw new IllegalStateException("You are adding too many system settings. "
1069                     + "You should stop using system settings for app specific data"
1070                     + " package: " + packageName);
1071         }
1072     }
1073 
1074     @GuardedBy("mLock")
1075     private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeyLength,
1076             String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) {
1077         if (isExemptFromMemoryUsageCap(packageName)) {
1078             return 0;
1079         }
1080         final int currentSize = mPackageToMemoryUsage.getOrDefault(packageName, 0);
1081         final int oldValueLength = (oldValue != null) ? oldValue.length() : 0;
1082         final int newValueLength = (newValue != null) ? newValue.length() : 0;
1083         final int oldDefaultValueLength = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
1084         final int newDefaultValueLength = (newDefaultValue != null) ? newDefaultValue.length() : 0;
1085         final int deltaSize = (deltaKeyLength + newValueLength + newDefaultValueLength
1086                 - oldValueLength - oldDefaultValueLength) * Character.BYTES;
1087         return Math.max(currentSize + deltaSize, 0);
1088     }
1089 
1090     @GuardedBy("mLock")
1091     private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
1092         if (isExemptFromMemoryUsageCap(packageName)) {
1093             return;
1094         }
1095         if (DEBUG) {
1096             Slog.i(LOG_TAG, "Settings for package: " + packageName
1097                     + " size: " + newSize + " bytes.");
1098         }
1099         mPackageToMemoryUsage.put(packageName, newSize);
1100     }
1101 
1102     public boolean hasSetting(String name) {
1103         synchronized (mLock) {
1104             return hasSettingLocked(name);
1105         }
1106     }
1107 
1108     @GuardedBy("mLock")
1109     private boolean hasSettingLocked(String name) {
1110         return mSettings.indexOfKey(name) >= 0;
1111     }
1112 
1113     @GuardedBy("mLock")
1114     private void scheduleWriteIfNeededLocked() {
1115         // If dirty then we have a write already scheduled.
1116         if (!mDirty) {
1117             mDirty = true;
1118             writeStateAsyncLocked();
1119         }
1120     }
1121 
1122     @GuardedBy("mLock")
1123     private void writeStateAsyncLocked() {
1124         final long currentTimeMillis = SystemClock.uptimeMillis();
1125 
1126         if (mWriteScheduled) {
1127             mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
1128 
1129             // If enough time passed, write without holding off anymore.
1130             final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
1131                     - mLastNotWrittenMutationTimeMillis;
1132             if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) {
1133                 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget();
1134                 return;
1135             }
1136 
1137             // Hold off a bit more as settings are frequently changing.
1138             final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis
1139                     + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0);
1140             final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis);
1141 
1142             Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS);
1143             mHandler.sendMessageDelayed(message, writeDelayMillis);
1144         } else {
1145             mLastNotWrittenMutationTimeMillis = currentTimeMillis;
1146             Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS);
1147             mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS);
1148             mWriteScheduled = true;
1149         }
1150     }
1151 
1152     private void doWriteState() {
1153         boolean wroteState = false;
1154         String settingFailedToBePersisted = null;
1155         final int version;
1156         final ArrayMap<String, Setting> settings;
1157         final ArrayMap<String, String> namespaceBannedHashes;
1158 
1159         synchronized (mLock) {
1160             version = mVersion;
1161             settings = new ArrayMap<>(mSettings);
1162             namespaceBannedHashes = new ArrayMap<>(mNamespaceBannedHashes);
1163             mDirty = false;
1164             mWriteScheduled = false;
1165         }
1166 
1167         synchronized (mWriteLock) {
1168             if (DEBUG_PERSISTENCE) {
1169                 Slog.i(LOG_TAG, "[PERSIST START]");
1170             }
1171 
1172             AtomicFile destination = new AtomicFile(mStatePersistFile, mStatePersistTag);
1173             FileOutputStream out = null;
1174             try {
1175                 out = destination.startWrite();
1176 
1177                 TypedXmlSerializer serializer = Xml.resolveSerializer(out);
1178                 serializer.startDocument(null, true);
1179                 serializer.startTag(null, TAG_SETTINGS);
1180                 serializer.attributeInt(null, ATTR_VERSION, version);
1181 
1182                 final int settingCount = settings.size();
1183                 for (int i = 0; i < settingCount; i++) {
1184                     Setting setting = settings.valueAt(i);
1185                     if (setting.isTransient()) {
1186                         if (DEBUG_PERSISTENCE) {
1187                             Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
1188                         }
1189                         continue;
1190                     }
1191 
1192                     try {
1193                         if (writeSingleSetting(
1194                                 mVersion,
1195                                 serializer,
1196                                 Long.toString(setting.getId()),
1197                                 setting.getName(),
1198                                 setting.getValue(), setting.getDefaultValue(),
1199                                 setting.getPackageName(),
1200                                 setting.getTag(), setting.isDefaultFromSystem(),
1201                                 setting.isValuePreservedInRestore())) {
1202                             if (DEBUG_PERSISTENCE) {
1203                                 Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "="
1204                                         + setting.getValue());
1205                             }
1206                         }
1207                     } catch (IOException ex) {
1208                         Slog.e(LOG_TAG, "[ABORT PERSISTING]" + setting.getName()
1209                                 + " due to error writing to disk", ex);
1210                         // A setting failed to be written. Abort the serialization to avoid leaving
1211                         // a partially serialized setting on disk, which can cause parsing errors.
1212                         // Note down the problematic setting, so that we can delete it before trying
1213                         // again to persist the rest of the settings.
1214                         settingFailedToBePersisted = setting.getName();
1215                         throw ex;
1216                     }
1217                 }
1218                 serializer.endTag(null, TAG_SETTINGS);
1219 
1220                 serializer.startTag(null, TAG_NAMESPACE_HASHES);
1221                 for (int i = 0; i < namespaceBannedHashes.size(); i++) {
1222                     String namespace = namespaceBannedHashes.keyAt(i);
1223                     String bannedHash = namespaceBannedHashes.get(namespace);
1224                     if (writeSingleNamespaceHash(serializer, namespace, bannedHash)) {
1225                         if (DEBUG_PERSISTENCE) {
1226                             Slog.i(LOG_TAG, "[PERSISTED] namespace=" + namespace
1227                                     + ", bannedHash=" + bannedHash);
1228                         }
1229                     }
1230                 }
1231                 serializer.endTag(null, TAG_NAMESPACE_HASHES);
1232                 serializer.endDocument();
1233                 destination.finishWrite(out);
1234 
1235                 wroteState = true;
1236 
1237                 if (DEBUG_PERSISTENCE) {
1238                     Slog.i(LOG_TAG, "[PERSIST END]");
1239                 }
1240             } catch (Throwable t) {
1241                 Slog.e(LOG_TAG, "Failed to write settings, restoring old file", t);
1242                 if (t instanceof IOException) {
1243                     if (t.getMessage().contains("Couldn't create directory")) {
1244                         if (DEBUG) {
1245                             // we failed to create a directory, so log the permissions and existence
1246                             // state for the settings file and directory
1247                             logSettingsDirectoryInformation(destination.getBaseFile());
1248                         }
1249                         // attempt to create the directory with Files.createDirectories, which
1250                         // throws more informative errors than File.mkdirs.
1251                         Path parentPath = destination.getBaseFile().getParentFile().toPath();
1252                         try {
1253                             Files.createDirectories(parentPath);
1254                             if (DEBUG) {
1255                                 Slog.i(LOG_TAG, "Successfully created " + parentPath);
1256                             }
1257                         } catch (Throwable t2) {
1258                             Slog.e(LOG_TAG, "Failed to write " + parentPath
1259                                     + " with Files.writeDirectories", t2);
1260                         }
1261                     }
1262                 }
1263                 destination.failWrite(out);
1264             } finally {
1265                 IoUtils.closeQuietly(out);
1266             }
1267         }
1268 
1269         if (!wroteState) {
1270             if (settingFailedToBePersisted != null) {
1271                 synchronized (mLock) {
1272                     // Delete the problematic setting. This will schedule a write as well.
1273                     deleteSettingLocked(settingFailedToBePersisted);
1274                 }
1275             }
1276         } else {
1277             // success
1278             synchronized (mLock) {
1279                 addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
1280             }
1281         }
1282     }
1283 
1284     private static void logSettingsDirectoryInformation(File settingsFile) {
1285         File parent = settingsFile.getParentFile();
1286         Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile
1287                 + " with stacktrace ", new Exception());
1288         File ancestorDir = parent;
1289         while (ancestorDir != null) {
1290             if (!ancestorDir.exists()) {
1291                 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
1292                         + " does not exist");
1293                 ancestorDir = ancestorDir.getParentFile();
1294             } else {
1295                 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
1296                         + " exists");
1297                 Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
1298                         + " permissions: r: " + ancestorDir.canRead() + " w: "
1299                         + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute());
1300                 File ancestorParent = ancestorDir.getParentFile();
1301                 if (ancestorParent != null) {
1302                     Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent
1303                             + " permissions: r: " + ancestorParent.canRead() + " w: "
1304                             + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute());
1305                 }
1306                 break;
1307             }
1308         }
1309     }
1310 
1311     static boolean writeSingleSetting(int version, TypedXmlSerializer serializer, String id,
1312             String name, String value, String defaultValue, String packageName,
1313             String tag, boolean defaultSysSet, boolean isValuePreservedInRestore)
1314             throws IOException {
1315         if (id == null || isBinary(id) || name == null || isBinary(name)
1316                 || packageName == null || isBinary(packageName)) {
1317             if (DEBUG_PERSISTENCE) {
1318                 Slog.w(LOG_TAG, "Invalid arguments for writeSingleSetting: version=" + version
1319                         + ", id=" + id + ", name=" + name + ", value=" + value
1320                         + ", defaultValue=" + defaultValue + ", packageName=" + packageName
1321                         + ", tag=" + tag + ", defaultSysSet=" + defaultSysSet
1322                         + ", isValuePreservedInRestore=" + isValuePreservedInRestore);
1323             }
1324             return false;
1325         }
1326         serializer.startTag(null, TAG_SETTING);
1327         serializer.attribute(null, ATTR_ID, id);
1328         serializer.attribute(null, ATTR_NAME, name);
1329         setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64,
1330                 version, serializer, value);
1331         serializer.attribute(null, ATTR_PACKAGE, packageName);
1332         if (defaultValue != null) {
1333             setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64,
1334                     version, serializer, defaultValue);
1335             serializer.attributeBoolean(null, ATTR_DEFAULT_SYS_SET, defaultSysSet);
1336             setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64,
1337                     version, serializer, tag);
1338         }
1339         if (isValuePreservedInRestore) {
1340             serializer.attributeBoolean(null, ATTR_PRESERVE_IN_RESTORE, true);
1341         }
1342         serializer.endTag(null, TAG_SETTING);
1343         return true;
1344     }
1345 
1346     static void setValueAttribute(String attr, String attrBase64, int version,
1347             TypedXmlSerializer serializer, String value) throws IOException {
1348         if (version >= SETTINGS_VERSION_NEW_ENCODING) {
1349             if (value == null) {
1350                 // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64.
1351             } else if (isBinary(value)) {
1352                 serializer.attribute(null, attrBase64, base64Encode(value));
1353             } else {
1354                 serializer.attribute(null, attr, value);
1355             }
1356         } else {
1357             // Old encoding.
1358             if (value == null) {
1359                 serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE);
1360             } else {
1361                 serializer.attribute(null, attr, value);
1362             }
1363         }
1364     }
1365 
1366     private static boolean writeSingleNamespaceHash(TypedXmlSerializer serializer, String namespace,
1367             String bannedHashCode) throws IOException {
1368         if (namespace == null || bannedHashCode == null) {
1369             if (DEBUG_PERSISTENCE) {
1370                 Slog.w(LOG_TAG, "Invalid arguments for writeSingleNamespaceHash: namespace="
1371                         + namespace + ", bannedHashCode=" + bannedHashCode);
1372             }
1373             return false;
1374         }
1375         serializer.startTag(null, TAG_NAMESPACE_HASH);
1376         serializer.attribute(null, ATTR_NAMESPACE, namespace);
1377         serializer.attribute(null, ATTR_BANNED_HASH, bannedHashCode);
1378         serializer.endTag(null, TAG_NAMESPACE_HASH);
1379         return true;
1380     }
1381 
1382     private static String hashCode(Map<String, String> keyValues) {
1383         return Integer.toString(keyValues.hashCode());
1384     }
1385 
1386     private String getValueAttribute(TypedXmlPullParser parser, String attr, String base64Attr) {
1387         if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) {
1388             final String value = parser.getAttributeValue(null, attr);
1389             if (value != null) {
1390                 return value;
1391             }
1392             final String base64 = parser.getAttributeValue(null, base64Attr);
1393             if (base64 != null) {
1394                 return base64Decode(base64);
1395             }
1396             // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64.
1397             return null;
1398         } else {
1399             // Old encoding.
1400             final String stored = parser.getAttributeValue(null, attr);
1401             if (NULL_VALUE_OLD_STYLE.equals(stored)) {
1402                 return null;
1403             } else {
1404                 return stored;
1405             }
1406         }
1407     }
1408 
1409     @GuardedBy("mLock")
1410     private void readStateSyncLocked() throws IllegalStateException {
1411         FileInputStream in;
1412         AtomicFile file = new AtomicFile(mStatePersistFile);
1413         try {
1414             in = file.openRead();
1415         } catch (FileNotFoundException fnfe) {
1416             Slog.w(LOG_TAG, "No settings state " + mStatePersistFile);
1417             if (DEBUG) {
1418                 logSettingsDirectoryInformation(mStatePersistFile);
1419             }
1420             addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
1421             return;
1422         }
1423         if (parseStateFromXmlStreamLocked(in)) {
1424             return;
1425         }
1426 
1427         // Settings file exists but is corrupted. Retry with the fallback file
1428         final File statePersistFallbackFile = new File(
1429                 mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX);
1430         Slog.w(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
1431                 + ", retrying with fallback file: " + statePersistFallbackFile);
1432         try {
1433             in = new AtomicFile(statePersistFallbackFile).openRead();
1434         } catch (FileNotFoundException fnfe) {
1435             final String message = "No fallback file found for: " + mStatePersistFile;
1436             Slog.wtf(LOG_TAG, message);
1437             if (!isConfigSettingsKey(mKey)) {
1438                 // Allow partially deserialized config settings because they can be updated later
1439                 throw new IllegalStateException(message);
1440             }
1441         }
1442         if (parseStateFromXmlStreamLocked(in)) {
1443             // Parsed state from fallback file. Restore original file with fallback file
1444             try {
1445                 FileUtils.copy(statePersistFallbackFile, mStatePersistFile);
1446             } catch (IOException ignored) {
1447                 // Failed to copy, but it's okay because we already parsed states from fallback file
1448             }
1449         } else {
1450             final String message = "Failed parsing settings file: " + mStatePersistFile;
1451             Slog.wtf(LOG_TAG, message);
1452             if (!isConfigSettingsKey(mKey)) {
1453                 // Allow partially deserialized config settings because they can be updated later
1454                 throw new IllegalStateException(message);
1455             }
1456         }
1457     }
1458 
1459     @GuardedBy("mLock")
1460     private boolean parseStateFromXmlStreamLocked(FileInputStream in) {
1461         try {
1462             TypedXmlPullParser parser = Xml.resolvePullParser(in);
1463             parseStateLocked(parser);
1464             return true;
1465         } catch (XmlPullParserException | IOException | NumberFormatException e) {
1466             Slog.e(LOG_TAG, "parse settings xml failed", e);
1467             return false;
1468         } finally {
1469             IoUtils.closeQuietly(in);
1470         }
1471     }
1472 
1473     /**
1474      * Uses AtomicFile to check if the file or its backup exists.
1475      *
1476      * @param file The file to check for existence
1477      * @return whether the original or backup exist
1478      */
1479     public static boolean stateFileExists(File file) {
1480         AtomicFile stateFile = new AtomicFile(file);
1481         return stateFile.exists();
1482     }
1483 
1484     private void parseStateLocked(TypedXmlPullParser parser)
1485             throws IOException, XmlPullParserException, NumberFormatException {
1486         final int outerDepth = parser.getDepth();
1487         int type;
1488         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1489                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1490             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1491                 continue;
1492             }
1493 
1494             String tagName = parser.getName();
1495             if (tagName.equals(TAG_SETTINGS)) {
1496                 parseSettingsLocked(parser);
1497             } else if (tagName.equals(TAG_NAMESPACE_HASHES)) {
1498                 parseNamespaceHash(parser);
1499             }
1500         }
1501     }
1502 
1503     /**
1504      * Transforms a staged flag name to its real flag name.
1505      *
1506      * Staged flags take the form {@code staged/namespace*flagName}. If
1507      * {@code stagedFlagName} takes the proper form, returns
1508      * {@code namespace/flagName}. Otherwise, returns {@code stagedFlagName}
1509      * unmodified, and logs an error message.
1510      *
1511      */
1512     @VisibleForTesting
1513     public static String createRealFlagName(String stagedFlagName) {
1514         int slashIndex = stagedFlagName.indexOf("/");
1515         if (slashIndex == -1 || slashIndex == stagedFlagName.length() - 1
1516                 || slashIndex == 0) {
1517             Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName);
1518             return stagedFlagName;
1519         }
1520 
1521         String namespaceAndFlag =
1522                 stagedFlagName.substring(slashIndex + 1);
1523 
1524         int starIndex = namespaceAndFlag.indexOf("*");
1525         if (starIndex == -1 || starIndex == namespaceAndFlag.length() - 1
1526                 || starIndex == 0) {
1527             Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName);
1528             return stagedFlagName;
1529         }
1530 
1531         String namespace =
1532                 namespaceAndFlag.substring(0, starIndex);
1533         String flagName =
1534                 namespaceAndFlag.substring(starIndex + 1);
1535 
1536         return namespace + "/" + flagName;
1537     }
1538 
1539     @GuardedBy("mLock")
1540     private void parseSettingsLocked(TypedXmlPullParser parser)
1541             throws IOException, XmlPullParserException, NumberFormatException {
1542 
1543         mVersion = parser.getAttributeInt(null, ATTR_VERSION);
1544 
1545         final int outerDepth = parser.getDepth();
1546         int type;
1547         HashSet<String> flagsWithStagedValueApplied = new HashSet<String>();
1548         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1549                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1550             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1551                 continue;
1552             }
1553 
1554             String tagName = parser.getName();
1555             if (tagName.equals(TAG_SETTING)) {
1556                 String id = parser.getAttributeValue(null, ATTR_ID);
1557                 String name = parser.getAttributeValue(null, ATTR_NAME);
1558                 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64);
1559                 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
1560                 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE,
1561                         ATTR_DEFAULT_VALUE_BASE64);
1562                 boolean isPreservedInRestore = parser.getAttributeBoolean(null,
1563                         ATTR_PRESERVE_IN_RESTORE, false);
1564                 String tag = null;
1565                 boolean fromSystem = false;
1566                 if (defaultValue != null) {
1567                     fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false);
1568                     tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);
1569                 }
1570 
1571                 if (isConfigSettingsKey(mKey)) {
1572                     if (flagsWithStagedValueApplied.contains(name)) {
1573                         continue;
1574                     }
1575 
1576                     if (name.startsWith(CONFIG_STAGED_PREFIX)) {
1577                         name = createRealFlagName(name);
1578                         flagsWithStagedValueApplied.add(name);
1579                     }
1580                 }
1581 
1582                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
1583                         fromSystem, Long.valueOf(id), isPreservedInRestore));
1584 
1585                 if (DEBUG_PERSISTENCE) {
1586                     Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
1587                 }
1588             }
1589         }
1590 
1591         if (isConfigSettingsKey(mKey) && !flagsWithStagedValueApplied.isEmpty()) {
1592             // On boot, the config table XML file includes special staged flags. On the initial
1593             // boot XML -> HashMap parse, these staged flags get transformed into real flags.
1594             // After this, the HashMap contains no special staged flags (only the transformed
1595             // real flags), but the XML still does. We then have no need for the special staged
1596             // flags in the XML, so we overwrite the XML with the latest contents of the
1597             // HashMap.
1598             writeStateAsyncLocked();
1599         }
1600     }
1601 
1602     @GuardedBy("mLock")
1603     private void parseNamespaceHash(TypedXmlPullParser parser)
1604             throws IOException, XmlPullParserException {
1605 
1606         final int outerDepth = parser.getDepth();
1607         int type;
1608         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1609                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1610             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1611                 continue;
1612             }
1613 
1614             if (parser.getName().equals(TAG_NAMESPACE_HASH)) {
1615                 String namespace = parser.getAttributeValue(null, ATTR_NAMESPACE);
1616                 String bannedHashCode = parser.getAttributeValue(null, ATTR_BANNED_HASH);
1617                 mNamespaceBannedHashes.put(namespace, bannedHashCode);
1618             }
1619         }
1620     }
1621 
1622     private static Map<String, String> removeNullValueOldStyle(Map<String, String> keyValues) {
1623         Iterator<Map.Entry<String, String>> it = keyValues.entrySet().iterator();
1624         while (it.hasNext()) {
1625             Map.Entry<String, String> keyValueEntry = it.next();
1626             if (NULL_VALUE_OLD_STYLE.equals(keyValueEntry.getValue())) {
1627                 keyValueEntry.setValue(null);
1628             }
1629         }
1630         return keyValues;
1631     }
1632 
1633     private final class MyHandler extends Handler {
1634         public static final int MSG_PERSIST_SETTINGS = 1;
1635 
1636         public MyHandler(Looper looper) {
1637             super(looper);
1638         }
1639 
1640         @Override
1641         public void handleMessage(Message message) {
1642             switch (message.what) {
1643                 case MSG_PERSIST_SETTINGS: {
1644                     Runnable callback = (Runnable) message.obj;
1645                     doWriteState();
1646                     if (callback != null) {
1647                         callback.run();
1648                     }
1649                 }
1650                 break;
1651             }
1652         }
1653     }
1654 
1655     private class HistoricalOperation {
1656         final long mTimestamp;
1657         final String mOperation;
1658         final Setting mSetting;
1659 
1660         public HistoricalOperation(long timestamp,
1661                 String operation, Setting setting) {
1662             mTimestamp = timestamp;
1663             mOperation = operation;
1664             mSetting = setting;
1665         }
1666     }
1667 
1668     public class Setting {
1669         private String name;
1670         private String value;
1671         private String defaultValue;
1672         private String packageName;
1673         private long id;
1674         private String tag;
1675         // Whether the default is set by the system
1676         private boolean defaultFromSystem;
1677         // Whether the value of this setting will be preserved when restore happens.
1678         private boolean isValuePreservedInRestore;
1679 
1680         public Setting(Setting other) {
1681             name = other.name;
1682             value = other.value;
1683             defaultValue = other.defaultValue;
1684             packageName = other.packageName;
1685             id = other.id;
1686             defaultFromSystem = other.defaultFromSystem;
1687             tag = other.tag;
1688             isValuePreservedInRestore = other.isValuePreservedInRestore;
1689         }
1690 
1691         public Setting(String name, String value, boolean makeDefault, String packageName,
1692                 String tag) {
1693             this(name, value, makeDefault, packageName, tag, false);
1694         }
1695 
1696         Setting(String name, String value, boolean makeDefault, String packageName,
1697                 String tag, boolean forceNonSystemPackage) {
1698             this.name = name;
1699             // overrideableByRestore = true as the first initialization isn't considered a
1700             // modification.
1701             update(value, makeDefault, packageName, tag, forceNonSystemPackage, true);
1702         }
1703 
1704         public Setting(String name, String value, String defaultValue,
1705                 String packageName, String tag, boolean fromSystem, long id) {
1706             this(name, value, defaultValue, packageName, tag, fromSystem, id,
1707                     /* isOverrideableByRestore */ false);
1708         }
1709 
1710         Setting(String name, String value, String defaultValue,
1711                 String packageName, String tag, boolean fromSystem, long id,
1712                 boolean isValuePreservedInRestore) {
1713             mNextId = Math.max(mNextId, id + 1);
1714             init(name, value, tag, defaultValue, packageName, fromSystem, id,
1715                     isValuePreservedInRestore);
1716         }
1717 
1718         private void init(String name, String value, String tag, String defaultValue,
1719                 String packageName, boolean fromSystem, long id,
1720                 boolean isValuePreservedInRestore) {
1721             this.name = name;
1722             this.value = internValue(value);
1723             this.tag = tag;
1724             this.defaultValue = internValue(defaultValue);
1725             this.packageName = TextUtils.safeIntern(packageName);
1726             this.id = id;
1727             this.defaultFromSystem = fromSystem;
1728             this.isValuePreservedInRestore = isValuePreservedInRestore;
1729         }
1730 
1731         public String getName() {
1732             return name;
1733         }
1734 
1735         public int getKey() {
1736             return mKey;
1737         }
1738 
1739         public String getValue() {
1740             return value;
1741         }
1742 
1743         public String getTag() {
1744             return tag;
1745         }
1746 
1747         public String getDefaultValue() {
1748             return defaultValue;
1749         }
1750 
1751         public String getPackageName() {
1752             return packageName;
1753         }
1754 
1755         public boolean isDefaultFromSystem() {
1756             return defaultFromSystem;
1757         }
1758 
1759         public boolean isValuePreservedInRestore() {
1760             return isValuePreservedInRestore;
1761         }
1762 
1763         public long getId() {
1764             return id;
1765         }
1766 
1767         public boolean isNull() {
1768             return false;
1769         }
1770 
1771         /** @return whether the value changed */
1772         public boolean reset() {
1773             // overrideableByRestore = true as resetting to default value isn't considered a
1774             // modification.
1775             return update(this.defaultValue, false, packageName, null, true, true,
1776                     /* resetToDefault */ true);
1777         }
1778 
1779         public boolean isTransient() {
1780             switch (getTypeFromKey(getKey())) {
1781                 case SETTINGS_TYPE_GLOBAL:
1782                     return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
1783             }
1784             return false;
1785         }
1786 
1787         public boolean update(String value, boolean setDefault, String packageName, String tag,
1788                 boolean forceNonSystemPackage, boolean overrideableByRestore) {
1789             return update(value, setDefault, packageName, tag, forceNonSystemPackage,
1790                     overrideableByRestore, /* resetToDefault */ false);
1791         }
1792 
1793         private boolean update(String value, boolean setDefault, String packageName, String tag,
1794                 boolean forceNonSystemPackage, boolean overrideableByRestore,
1795                 boolean resetToDefault) {
1796             final boolean callerSystem = !forceNonSystemPackage &&
1797                     !isNull() && (isCalledFromSystem(packageName)
1798                     || isSystemPackage(mContext, packageName));
1799             // Settings set by the system are always defaults.
1800             if (callerSystem) {
1801                 setDefault = true;
1802             }
1803 
1804             String defaultValue = this.defaultValue;
1805             boolean defaultFromSystem = this.defaultFromSystem;
1806             if (setDefault) {
1807                 if (!Objects.equals(value, this.defaultValue)
1808                         && (!defaultFromSystem || callerSystem)) {
1809                     defaultValue = value;
1810                     // Default null means no default, so the tag is irrelevant
1811                     // since it is used to reset a settings subset their defaults.
1812                     // Also it is irrelevant if the system set the canonical default.
1813                     if (defaultValue == null) {
1814                         tag = null;
1815                         defaultFromSystem = false;
1816                     }
1817                 }
1818                 if (!defaultFromSystem && value != null) {
1819                     if (callerSystem) {
1820                         defaultFromSystem = true;
1821                     }
1822                 }
1823             }
1824 
1825             // isValuePreservedInRestore shouldn't change back to false if it has been set to true.
1826             boolean isPreserved = shouldPreserveSetting(overrideableByRestore, resetToDefault,
1827                     packageName, value);
1828 
1829             // Is something gonna change?
1830             if (Objects.equals(value, this.value)
1831                     && Objects.equals(defaultValue, this.defaultValue)
1832                     && Objects.equals(packageName, this.packageName)
1833                     && Objects.equals(tag, this.tag)
1834                     && defaultFromSystem == this.defaultFromSystem
1835                     && isPreserved == this.isValuePreservedInRestore) {
1836                 return false;
1837             }
1838 
1839             init(name, value, tag, defaultValue, packageName, defaultFromSystem,
1840                     mNextId++, isPreserved);
1841 
1842             return true;
1843         }
1844 
1845         public String toString() {
1846             return "Setting{name=" + name + " value=" + value
1847                     + (defaultValue != null ? " default=" + defaultValue : "")
1848                     + " packageName=" + packageName + " tag=" + tag
1849                     + " defaultFromSystem=" + defaultFromSystem + "}";
1850         }
1851 
1852         /**
1853          * Interns a string if it's a common setting value.
1854          * Otherwise returns the given string.
1855          */
1856         static String internValue(String str) {
1857             if (str == null) {
1858                 return null;
1859             }
1860             switch (str) {
1861                 case "true":
1862                     return "true";
1863                 case "false":
1864                     return "false";
1865                 case "0":
1866                     return "0";
1867                 case "1":
1868                     return "1";
1869                 case "":
1870                     return "";
1871                 case "null":
1872                     return null;  // explicit null has special handling
1873                 default:
1874                     return str;
1875             }
1876         }
1877 
1878         private boolean shouldPreserveSetting(boolean overrideableByRestore,
1879                 boolean resetToDefault, String packageName, String value) {
1880             if (resetToDefault) {
1881                 // By default settings are not marked as preserved.
1882                 return false;
1883             }
1884             if (value != null && value.equals(this.value)
1885                     && SYSTEM_PACKAGE_NAME.equals(packageName)) {
1886                 // Do not mark preserved if it's the system reinitializing to the same value.
1887                 return false;
1888             }
1889 
1890             // isValuePreservedInRestore shouldn't change back to false if it has been set to true.
1891             return this.isValuePreservedInRestore || !overrideableByRestore;
1892         }
1893     }
1894 
1895     /**
1896      * @return TRUE if a string is considered "binary" from KXML's point of view.  NOTE DO NOT
1897      * pass null.
1898      */
1899     public static boolean isBinary(String s) {
1900         if (s == null) {
1901             throw new NullPointerException();
1902         }
1903         // See KXmlSerializer.writeEscaped
1904         for (int i = 0; i < s.length(); i++) {
1905             char c = s.charAt(i);
1906             boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
1907             if (!allowedInXml) {
1908                 return true;
1909             }
1910         }
1911         return false;
1912     }
1913 
1914     private static String base64Encode(String s) {
1915         return Base64.encodeToString(toBytes(s), Base64.NO_WRAP);
1916     }
1917 
1918     private static String base64Decode(String s) {
1919         return fromBytes(Base64.decode(s, Base64.DEFAULT));
1920     }
1921 
1922     // Note the followings are basically just UTF-16 encode/decode.  But we want to preserve
1923     // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves,
1924     // since I don't know how Charset would treat them.
1925 
1926     private static byte[] toBytes(String s) {
1927         final byte[] result = new byte[s.length() * 2];
1928         int resultIndex = 0;
1929         for (int i = 0; i < s.length(); ++i) {
1930             char ch = s.charAt(i);
1931             result[resultIndex++] = (byte) (ch >> 8);
1932             result[resultIndex++] = (byte) ch;
1933         }
1934         return result;
1935     }
1936 
1937     private static String fromBytes(byte[] bytes) {
1938         final StringBuilder sb = new StringBuilder(bytes.length / 2);
1939 
1940         final int last = bytes.length - 1;
1941 
1942         for (int i = 0; i < last; i += 2) {
1943             final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff));
1944             sb.append(ch);
1945         }
1946         return sb.toString();
1947     }
1948 
1949     // Cache the list of names of system packages. This is only called once on system boot.
1950     public static void cacheSystemPackageNamesAndSystemSignature(@NonNull Context context) {
1951         final PackageManager packageManager = context.getPackageManager();
1952         final long identity = Binder.clearCallingIdentity();
1953         try {
1954             sSystemPackages.add(SYSTEM_PACKAGE_NAME);
1955             // Cache SetupWizard package name.
1956             final String setupWizPackageName = packageManager.getSetupWizardPackageName();
1957             if (setupWizPackageName != null) {
1958                 sSystemPackages.add(setupWizPackageName);
1959             }
1960             final List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
1961             final int installedPackagesCount = packageInfos.size();
1962             for (int i = 0; i < installedPackagesCount; i++) {
1963                 if (shouldAddToSystemPackages(packageInfos.get(i))) {
1964                     sSystemPackages.add(packageInfos.get(i).packageName);
1965                 }
1966             }
1967         } finally {
1968             Binder.restoreCallingIdentity(identity);
1969         }
1970     }
1971 
1972     private static boolean shouldAddToSystemPackages(@NonNull PackageInfo packageInfo) {
1973         // Shell and Root are not considered a part of the system
1974         if (isShellOrRoot(packageInfo.packageName)) {
1975             return false;
1976         }
1977         // Already added
1978         if (sSystemPackages.contains(packageInfo.packageName)) {
1979             return false;
1980         }
1981         return isSystemPackage(packageInfo.applicationInfo);
1982     }
1983 
1984     private static boolean isShellOrRoot(@NonNull String packageName) {
1985         return (SHELL_PACKAGE_NAME.equals(packageName)
1986                 || ROOT_PACKAGE_NAME.equals(packageName));
1987     }
1988 
1989     private static boolean isCalledFromSystem(@NonNull String packageName) {
1990         // Shell and Root are not considered a part of the system
1991         if (isShellOrRoot(packageName)) {
1992             return false;
1993         }
1994         final int callingUid = Binder.getCallingUid();
1995         // Native services running as a special UID get a pass
1996         final int callingAppId = UserHandle.getAppId(callingUid);
1997         return (callingAppId < FIRST_APPLICATION_UID);
1998     }
1999 
2000     public static boolean isSystemPackage(@NonNull Context context, @NonNull String packageName) {
2001         // Check shell or root before trying to retrieve ApplicationInfo to fail fast
2002         if (isShellOrRoot(packageName)) {
2003             return false;
2004         }
2005         // If it's a known system package or known to be platform signed
2006         if (sSystemPackages.contains(packageName)) {
2007             return true;
2008         }
2009 
2010         ApplicationInfo aInfo = null;
2011         final long identity = Binder.clearCallingIdentity();
2012         try {
2013             try {
2014                 // Notice that this makes a call to package manager inside the lock
2015                 aInfo = context.getPackageManager().getApplicationInfo(packageName, 0);
2016             } catch (PackageManager.NameNotFoundException ignored) {
2017             }
2018         } finally {
2019             Binder.restoreCallingIdentity(identity);
2020         }
2021         return isSystemPackage(aInfo);
2022     }
2023 
2024     private static boolean isSystemPackage(@Nullable ApplicationInfo aInfo) {
2025         if (aInfo == null) {
2026             return false;
2027         }
2028         // If the system or a special system UID (like telephony), done.
2029         if (aInfo.uid < FIRST_APPLICATION_UID) {
2030             return true;
2031         }
2032         // If a persistent system app, done.
2033         if ((aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
2034                 && (aInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
2035             return true;
2036         }
2037         // Platform signed packages are considered to be from the system
2038         if (aInfo.isSignedWithPlatformKey()) {
2039             return true;
2040         }
2041         return false;
2042     }
2043 
2044     @VisibleForTesting
2045     public int getMemoryUsage(String packageName) {
2046         synchronized (mLock) {
2047             return mPackageToMemoryUsage.getOrDefault(packageName, 0);
2048         }
2049     }
2050 
2051     /**
2052      * Allow tests to wait for the handler to finish handling all the remaining messages
2053      */
2054     @VisibleForTesting
2055     public void waitForHandler() {
2056         final CountDownLatch latch = new CountDownLatch(1);
2057         synchronized (mLock) {
2058             mHandler.post(latch::countDown);
2059         }
2060         try {
2061             latch.await();
2062         } catch (InterruptedException e) {
2063             // ignored
2064         }
2065     }
2066 }
2067