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