1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.settings;
18 
19 import static android.os.Process.FIRST_APPLICATION_UID;
20 import static android.os.Process.INVALID_UID;
21 
22 import android.annotation.NonNull;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManagerInternal;
28 import android.content.pm.Signature;
29 import android.os.Binder;
30 import android.os.Build;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.SystemClock;
35 import android.os.UserHandle;
36 import android.provider.Settings;
37 import android.provider.Settings.Global;
38 import android.providers.settings.SettingsOperationProto;
39 import android.text.TextUtils;
40 import android.util.ArrayMap;
41 import android.util.AtomicFile;
42 import android.util.Base64;
43 import android.util.Slog;
44 import android.util.SparseIntArray;
45 import android.util.StatsLog;
46 import android.util.TimeUtils;
47 import android.util.Xml;
48 import android.util.proto.ProtoOutputStream;
49 
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.util.ArrayUtils;
52 import com.android.server.LocalServices;
53 
54 import libcore.io.IoUtils;
55 
56 import org.xmlpull.v1.XmlPullParser;
57 import org.xmlpull.v1.XmlPullParserException;
58 import org.xmlpull.v1.XmlSerializer;
59 
60 import java.io.File;
61 import java.io.FileInputStream;
62 import java.io.FileNotFoundException;
63 import java.io.FileOutputStream;
64 import java.io.IOException;
65 import java.io.PrintWriter;
66 import java.nio.charset.StandardCharsets;
67 import java.util.ArrayList;
68 import java.util.List;
69 import java.util.Objects;
70 
71 /**
72  * This class contains the state for one type of settings. It is responsible
73  * for saving the state asynchronously to an XML file after a mutation and
74  * loading the from an XML file on construction.
75  * <p>
76  * This class uses the same lock as the settings provider to ensure that
77  * multiple changes made by the settings provider, e,g, upgrade, bulk insert,
78  * etc, are atomically persisted since the asynchronous persistence is using
79  * the same lock to grab the current state to write to disk.
80  * </p>
81  */
82 final class SettingsState {
83     private static final boolean DEBUG = false;
84     private static final boolean DEBUG_PERSISTENCE = false;
85 
86     private static final String LOG_TAG = "SettingsState";
87 
88     static final String SYSTEM_PACKAGE_NAME = "android";
89 
90     static final int SETTINGS_VERSION_NEW_ENCODING = 121;
91 
92     private static final long WRITE_SETTINGS_DELAY_MILLIS = 200;
93     private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000;
94 
95     public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1;
96     public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 20000;
97 
98     public static final int VERSION_UNDEFINED = -1;
99 
100     private static final String TAG_SETTINGS = "settings";
101     private static final String TAG_SETTING = "setting";
102     private static final String ATTR_PACKAGE = "package";
103     private static final String ATTR_DEFAULT_SYS_SET = "defaultSysSet";
104     private static final String ATTR_TAG = "tag";
105     private static final String ATTR_TAG_BASE64 = "tagBase64";
106 
107     private static final String ATTR_VERSION = "version";
108     private static final String ATTR_ID = "id";
109     private static final String ATTR_NAME = "name";
110 
111     /**
112      * Non-binary value will be written in this attributes.
113      */
114     private static final String ATTR_VALUE = "value";
115     private static final String ATTR_DEFAULT_VALUE = "defaultValue";
116 
117     /**
118      * KXmlSerializer won't like some characters. We encode such characters
119      * in base64 and store in this attribute.
120      * NOTE: A null value will have *neither* ATTR_VALUE nor ATTR_VALUE_BASE64.
121      */
122     private static final String ATTR_VALUE_BASE64 = "valueBase64";
123     private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64";
124 
125     // This was used in version 120 and before.
126     private static final String NULL_VALUE_OLD_STYLE = "null";
127 
128     private static final int HISTORICAL_OPERATION_COUNT = 20;
129     private static final String HISTORICAL_OPERATION_UPDATE = "update";
130     private static final String HISTORICAL_OPERATION_DELETE = "delete";
131     private static final String HISTORICAL_OPERATION_PERSIST = "persist";
132     private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize";
133     private static final String HISTORICAL_OPERATION_RESET = "reset";
134 
135     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
136     private static final String ROOT_PACKAGE_NAME = "root";
137 
138     private static final String NULL_VALUE = "null";
139 
140     private static final Object sLock = new Object();
141 
142     @GuardedBy("sLock")
143     private static final SparseIntArray sSystemUids = new SparseIntArray();
144 
145     @GuardedBy("sLock")
146     private static Signature sSystemSignature;
147 
148     private final Object mWriteLock = new Object();
149 
150     private final Object mLock;
151 
152     private final Handler mHandler;
153 
154     @GuardedBy("mLock")
155     private final Context mContext;
156 
157     @GuardedBy("mLock")
158     private final ArrayMap<String, Setting> mSettings = new ArrayMap<>();
159 
160     @GuardedBy("mLock")
161     private final ArrayMap<String, Integer> mPackageToMemoryUsage;
162 
163     @GuardedBy("mLock")
164     private final int mMaxBytesPerAppPackage;
165 
166     @GuardedBy("mLock")
167     private final File mStatePersistFile;
168 
169     @GuardedBy("mLock")
170     private final String mStatePersistTag;
171 
172     private final Setting mNullSetting = new Setting(null, null, false, null, null) {
173         @Override
174         public boolean isNull() {
175             return true;
176         }
177     };
178 
179     @GuardedBy("mLock")
180     private final List<HistoricalOperation> mHistoricalOperations;
181 
182     @GuardedBy("mLock")
183     public final int mKey;
184 
185     @GuardedBy("mLock")
186     private int mVersion = VERSION_UNDEFINED;
187 
188     @GuardedBy("mLock")
189     private long mLastNotWrittenMutationTimeMillis;
190 
191     @GuardedBy("mLock")
192     private boolean mDirty;
193 
194     @GuardedBy("mLock")
195     private boolean mWriteScheduled;
196 
197     @GuardedBy("mLock")
198     private long mNextId;
199 
200     @GuardedBy("mLock")
201     private int mNextHistoricalOpIdx;
202 
203     public static final int SETTINGS_TYPE_GLOBAL = 0;
204     public static final int SETTINGS_TYPE_SYSTEM = 1;
205     public static final int SETTINGS_TYPE_SECURE = 2;
206     public static final int SETTINGS_TYPE_SSAID = 3;
207     public static final int SETTINGS_TYPE_CONFIG = 4;
208 
209     public static final int SETTINGS_TYPE_MASK = 0xF0000000;
210     public static final int SETTINGS_TYPE_SHIFT = 28;
211 
makeKey(int type, int userId)212     public static int makeKey(int type, int userId) {
213         return (type << SETTINGS_TYPE_SHIFT) | userId;
214     }
215 
getTypeFromKey(int key)216     public static int getTypeFromKey(int key) {
217         return key >>> SETTINGS_TYPE_SHIFT;
218     }
219 
getUserIdFromKey(int key)220     public static int getUserIdFromKey(int key) {
221         return key & ~SETTINGS_TYPE_MASK;
222     }
223 
settingTypeToString(int type)224     public static String settingTypeToString(int type) {
225         switch (type) {
226             case SETTINGS_TYPE_CONFIG: {
227                 return "SETTINGS_CONFIG";
228             }
229             case SETTINGS_TYPE_GLOBAL: {
230                 return "SETTINGS_GLOBAL";
231             }
232             case SETTINGS_TYPE_SECURE: {
233                 return "SETTINGS_SECURE";
234             }
235             case SETTINGS_TYPE_SYSTEM: {
236                 return "SETTINGS_SYSTEM";
237             }
238             case SETTINGS_TYPE_SSAID: {
239                 return "SETTINGS_SSAID";
240             }
241             default: {
242                 return "UNKNOWN";
243             }
244         }
245     }
246 
keyToString(int key)247     public static String keyToString(int key) {
248         return "Key[user=" + getUserIdFromKey(key) + ";type="
249                 + settingTypeToString(getTypeFromKey(key)) + "]";
250     }
251 
SettingsState(Context context, Object lock, File file, int key, int maxBytesPerAppPackage, Looper looper)252     public SettingsState(Context context, Object lock, File file, int key,
253             int maxBytesPerAppPackage, Looper looper) {
254         // It is important that we use the same lock as the settings provider
255         // to ensure multiple mutations on this state are atomicaly persisted
256         // as the async persistence should be blocked while we make changes.
257         mContext = context;
258         mLock = lock;
259         mStatePersistFile = file;
260         mStatePersistTag = "settings-" + getTypeFromKey(key) + "-" + getUserIdFromKey(key);
261         mKey = key;
262         mHandler = new MyHandler(looper);
263         if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) {
264             mMaxBytesPerAppPackage = maxBytesPerAppPackage;
265             mPackageToMemoryUsage = new ArrayMap<>();
266         } else {
267             mMaxBytesPerAppPackage = maxBytesPerAppPackage;
268             mPackageToMemoryUsage = null;
269         }
270 
271         mHistoricalOperations = Build.IS_DEBUGGABLE
272                 ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
273 
274         synchronized (mLock) {
275             readStateSyncLocked();
276         }
277     }
278 
279     // The settings provider must hold its lock when calling here.
280     @GuardedBy("mLock")
getVersionLocked()281     public int getVersionLocked() {
282         return mVersion;
283     }
284 
getNullSetting()285     public Setting getNullSetting() {
286         return mNullSetting;
287     }
288 
289     // The settings provider must hold its lock when calling here.
290     @GuardedBy("mLock")
setVersionLocked(int version)291     public void setVersionLocked(int version) {
292         if (version == mVersion) {
293             return;
294         }
295         mVersion = version;
296 
297         scheduleWriteIfNeededLocked();
298     }
299 
300     // The settings provider must hold its lock when calling here.
301     @GuardedBy("mLock")
removeSettingsForPackageLocked(String packageName)302     public void removeSettingsForPackageLocked(String packageName) {
303         boolean removedSomething = false;
304 
305         final int settingCount = mSettings.size();
306         for (int i = settingCount - 1; i >= 0; i--) {
307             String name = mSettings.keyAt(i);
308             // Settings defined by us are never dropped.
309             if (Settings.System.PUBLIC_SETTINGS.contains(name)
310                     || Settings.System.PRIVATE_SETTINGS.contains(name)) {
311                 continue;
312             }
313             Setting setting = mSettings.valueAt(i);
314             if (packageName.equals(setting.packageName)) {
315                 mSettings.removeAt(i);
316                 removedSomething = true;
317             }
318         }
319 
320         if (removedSomething) {
321             scheduleWriteIfNeededLocked();
322         }
323     }
324 
325     // The settings provider must hold its lock when calling here.
326     @GuardedBy("mLock")
getSettingNamesLocked()327     public List<String> getSettingNamesLocked() {
328         ArrayList<String> names = new ArrayList<>();
329         final int settingsCount = mSettings.size();
330         for (int i = 0; i < settingsCount; i++) {
331             String name = mSettings.keyAt(i);
332             names.add(name);
333         }
334         return names;
335     }
336 
337     // The settings provider must hold its lock when calling here.
338     @GuardedBy("mLock")
getSettingLocked(String name)339     public Setting getSettingLocked(String name) {
340         if (TextUtils.isEmpty(name)) {
341             return mNullSetting;
342         }
343         Setting setting = mSettings.get(name);
344         if (setting != null) {
345             return new Setting(setting);
346         }
347         return mNullSetting;
348     }
349 
350     // The settings provider must hold its lock when calling here.
updateSettingLocked(String name, String value, String tag, boolean makeValue, String packageName)351     public boolean updateSettingLocked(String name, String value, String tag,
352             boolean makeValue, String packageName) {
353         if (!hasSettingLocked(name)) {
354             return false;
355         }
356 
357         return insertSettingLocked(name, value, tag, makeValue, packageName);
358     }
359 
360     // The settings provider must hold its lock when calling here.
361     @GuardedBy("mLock")
resetSettingDefaultValueLocked(String name)362     public void resetSettingDefaultValueLocked(String name) {
363         Setting oldSetting = getSettingLocked(name);
364         if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) {
365             String oldValue = oldSetting.getValue();
366             String oldDefaultValue = oldSetting.getDefaultValue();
367             Setting newSetting = new Setting(name, oldSetting.getValue(), null,
368                     oldSetting.getPackageName(), oldSetting.getTag(), false,
369                     oldSetting.getId());
370             mSettings.put(name, newSetting);
371             updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
372                     newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
373             scheduleWriteIfNeededLocked();
374         }
375     }
376 
377     // The settings provider must hold its lock when calling here.
378     @GuardedBy("mLock")
insertSettingLocked(String name, String value, String tag, boolean makeDefault, String packageName)379     public boolean insertSettingLocked(String name, String value, String tag,
380             boolean makeDefault, String packageName) {
381         return insertSettingLocked(name, value, tag, makeDefault, false, packageName);
382     }
383 
384     // The settings provider must hold its lock when calling here.
385     @GuardedBy("mLock")
insertSettingLocked(String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName)386     public boolean insertSettingLocked(String name, String value, String tag,
387             boolean makeDefault, boolean forceNonSystemPackage, String packageName) {
388         if (TextUtils.isEmpty(name)) {
389             return false;
390         }
391 
392         Setting oldState = mSettings.get(name);
393         String oldValue = (oldState != null) ? oldState.value : null;
394         String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
395         Setting newState;
396 
397         if (oldState != null) {
398             if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) {
399                 return false;
400             }
401             newState = oldState;
402         } else {
403             newState = new Setting(name, value, makeDefault, packageName, tag);
404             mSettings.put(name, newState);
405         }
406 
407         StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag,
408             makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
409 
410         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
411 
412         updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
413                 oldDefaultValue, newState.getDefaultValue());
414 
415         scheduleWriteIfNeededLocked();
416 
417         return true;
418     }
419 
420     // The settings provider must hold its lock when calling here.
persistSyncLocked()421     public void persistSyncLocked() {
422         mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
423         doWriteState();
424     }
425 
426     // The settings provider must hold its lock when calling here.
427     @GuardedBy("mLock")
deleteSettingLocked(String name)428     public boolean deleteSettingLocked(String name) {
429         if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
430             return false;
431         }
432 
433         Setting oldState = mSettings.remove(name);
434 
435         StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
436             oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
437             StatsLog.SETTING_CHANGED__REASON__DELETED);
438 
439         updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
440                 null, oldState.defaultValue, null);
441 
442         addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
443 
444         scheduleWriteIfNeededLocked();
445 
446         return true;
447     }
448 
449     // The settings provider must hold its lock when calling here.
450     @GuardedBy("mLock")
resetSettingLocked(String name)451     public boolean resetSettingLocked(String name) {
452         if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
453             return false;
454         }
455 
456         Setting setting = mSettings.get(name);
457 
458         Setting oldSetting = new Setting(setting);
459         String oldValue = setting.getValue();
460         String oldDefaultValue = setting.getDefaultValue();
461 
462         if (!setting.reset()) {
463             return false;
464         }
465 
466         String newValue = setting.getValue();
467         String newDefaultValue = setting.getDefaultValue();
468 
469         updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
470                 newValue, oldDefaultValue, newDefaultValue);
471 
472         addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
473 
474         scheduleWriteIfNeededLocked();
475 
476         return true;
477     }
478 
479     // The settings provider must hold its lock when calling here.
480     @GuardedBy("mLock")
destroyLocked(Runnable callback)481     public void destroyLocked(Runnable callback) {
482         mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
483         if (callback != null) {
484             if (mDirty) {
485                 // Do it without a delay.
486                 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS,
487                         callback).sendToTarget();
488                 return;
489             }
490             callback.run();
491         }
492     }
493 
494     @GuardedBy("mLock")
addHistoricalOperationLocked(String type, Setting setting)495     private void addHistoricalOperationLocked(String type, Setting setting) {
496         if (mHistoricalOperations == null) {
497             return;
498         }
499         HistoricalOperation operation = new HistoricalOperation(
500                 SystemClock.elapsedRealtime(), type,
501                 setting != null ? new Setting(setting) : null);
502         if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) {
503             mHistoricalOperations.add(operation);
504         } else {
505             mHistoricalOperations.set(mNextHistoricalOpIdx, operation);
506         }
507         mNextHistoricalOpIdx++;
508         if (mNextHistoricalOpIdx >= HISTORICAL_OPERATION_COUNT) {
509             mNextHistoricalOpIdx = 0;
510         }
511     }
512 
513     /**
514      * Dump historical operations as a proto buf.
515      *
516      * @param proto The proto buf stream to dump to
517      * @param fieldId The repeated field ID to use to save an operation to.
518      */
dumpHistoricalOperations(@onNull ProtoOutputStream proto, long fieldId)519     void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) {
520         synchronized (mLock) {
521             if (mHistoricalOperations == null) {
522                 return;
523             }
524 
525             final int operationCount = mHistoricalOperations.size();
526             for (int i = 0; i < operationCount; i++) {
527                 int index = mNextHistoricalOpIdx - 1 - i;
528                 if (index < 0) {
529                     index = operationCount + index;
530                 }
531                 HistoricalOperation operation = mHistoricalOperations.get(index);
532 
533                 final long token = proto.start(fieldId);
534                 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp);
535                 proto.write(SettingsOperationProto.OPERATION, operation.mOperation);
536                 if (operation.mSetting != null) {
537                     // Only add the name of the setting, since we don't know the historical package
538                     // and values for it so they would be misleading to add here (all we could
539                     // add is what the current data is).
540                     proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName());
541                 }
542                 proto.end(token);
543             }
544         }
545     }
546 
dumpHistoricalOperations(PrintWriter pw)547     public void dumpHistoricalOperations(PrintWriter pw) {
548         synchronized (mLock) {
549             if (mHistoricalOperations == null) {
550                 return;
551             }
552             pw.println("Historical operations");
553             final int operationCount = mHistoricalOperations.size();
554             for (int i = 0; i < operationCount; i++) {
555                 int index = mNextHistoricalOpIdx - 1 - i;
556                 if (index < 0) {
557                     index = operationCount + index;
558                 }
559                 HistoricalOperation operation = mHistoricalOperations.get(index);
560                 pw.print(TimeUtils.formatForLogging(operation.mTimestamp));
561                 pw.print(" ");
562                 pw.print(operation.mOperation);
563                 if (operation.mSetting != null) {
564                     pw.print(" ");
565                     // Only print the name of the setting, since we don't know the
566                     // historical package and values for it so they would be misleading
567                     // to print here (all we could print is what the current data is).
568                     pw.print(operation.mSetting.getName());
569                 }
570                 pw.println();
571             }
572             pw.println();
573             pw.println();
574         }
575     }
576 
577     @GuardedBy("mLock")
updateMemoryUsagePerPackageLocked(String packageName, String oldValue, String newValue, String oldDefaultValue, String newDefaultValue)578     private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
579             String newValue, String oldDefaultValue, String newDefaultValue) {
580         if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
581             return;
582         }
583 
584         if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
585             return;
586         }
587 
588         final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
589         final int newValueSize = (newValue != null) ? newValue.length() : 0;
590         final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
591         final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
592         final int deltaSize = newValueSize + newDefaultValueSize
593                 - oldValueSize - oldDefaultValueSize;
594 
595         Integer currentSize = mPackageToMemoryUsage.get(packageName);
596         final int newSize = Math.max((currentSize != null)
597                 ? currentSize + deltaSize : deltaSize, 0);
598 
599         if (newSize > mMaxBytesPerAppPackage) {
600             throw new IllegalStateException("You are adding too many system settings. "
601                     + "You should stop using system settings for app specific data"
602                     + " package: " + packageName);
603         }
604 
605         if (DEBUG) {
606             Slog.i(LOG_TAG, "Settings for package: " + packageName
607                     + " size: " + newSize + " bytes.");
608         }
609 
610         mPackageToMemoryUsage.put(packageName, newSize);
611     }
612 
613     @GuardedBy("mLock")
hasSettingLocked(String name)614     private boolean hasSettingLocked(String name) {
615         return mSettings.indexOfKey(name) >= 0;
616     }
617 
618     @GuardedBy("mLock")
scheduleWriteIfNeededLocked()619     private void scheduleWriteIfNeededLocked() {
620         // If dirty then we have a write already scheduled.
621         if (!mDirty) {
622             mDirty = true;
623             writeStateAsyncLocked();
624         }
625     }
626 
627     @GuardedBy("mLock")
writeStateAsyncLocked()628     private void writeStateAsyncLocked() {
629         final long currentTimeMillis = SystemClock.uptimeMillis();
630 
631         if (mWriteScheduled) {
632             mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
633 
634             // If enough time passed, write without holding off anymore.
635             final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
636                     - mLastNotWrittenMutationTimeMillis;
637             if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) {
638                 mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget();
639                 return;
640             }
641 
642             // Hold off a bit more as settings are frequently changing.
643             final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis
644                     + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0);
645             final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis);
646 
647             Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS);
648             mHandler.sendMessageDelayed(message, writeDelayMillis);
649         } else {
650             mLastNotWrittenMutationTimeMillis = currentTimeMillis;
651             Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS);
652             mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS);
653             mWriteScheduled = true;
654         }
655     }
656 
doWriteState()657     private void doWriteState() {
658         boolean wroteState = false;
659         final int version;
660         final ArrayMap<String, Setting> settings;
661 
662         synchronized (mLock) {
663             version = mVersion;
664             settings = new ArrayMap<>(mSettings);
665             mDirty = false;
666             mWriteScheduled = false;
667         }
668 
669         synchronized (mWriteLock) {
670             if (DEBUG_PERSISTENCE) {
671                 Slog.i(LOG_TAG, "[PERSIST START]");
672             }
673 
674             AtomicFile destination = new AtomicFile(mStatePersistFile, mStatePersistTag);
675             FileOutputStream out = null;
676             try {
677                 out = destination.startWrite();
678 
679                 XmlSerializer serializer = Xml.newSerializer();
680                 serializer.setOutput(out, StandardCharsets.UTF_8.name());
681                 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
682                         true);
683                 serializer.startDocument(null, true);
684                 serializer.startTag(null, TAG_SETTINGS);
685                 serializer.attribute(null, ATTR_VERSION, String.valueOf(version));
686 
687                 final int settingCount = settings.size();
688                 for (int i = 0; i < settingCount; i++) {
689                     Setting setting = settings.valueAt(i);
690 
691                     if (setting.isTransient()) {
692                         if (DEBUG_PERSISTENCE) {
693                             Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
694                         }
695                         continue;
696                     }
697 
698                     writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
699                             setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
700                             setting.getTag(), setting.isDefaultFromSystem());
701 
702                     if (DEBUG_PERSISTENCE) {
703                         Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "="
704                                 + setting.getValue());
705                     }
706                 }
707 
708                 serializer.endTag(null, TAG_SETTINGS);
709                 serializer.endDocument();
710                 destination.finishWrite(out);
711 
712                 wroteState = true;
713 
714                 if (DEBUG_PERSISTENCE) {
715                     Slog.i(LOG_TAG, "[PERSIST END]");
716                 }
717             } catch (Throwable t) {
718                 Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
719                 destination.failWrite(out);
720             } finally {
721                 IoUtils.closeQuietly(out);
722             }
723         }
724 
725         if (wroteState) {
726             synchronized (mLock) {
727                 addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
728             }
729         }
730     }
731 
writeSingleSetting(int version, XmlSerializer serializer, String id, String name, String value, String defaultValue, String packageName, String tag, boolean defaultSysSet)732     static void writeSingleSetting(int version, XmlSerializer serializer, String id,
733             String name, String value, String defaultValue, String packageName,
734             String tag, boolean defaultSysSet) throws IOException {
735         if (id == null || isBinary(id) || name == null || isBinary(name)
736                 || packageName == null || isBinary(packageName)) {
737             // This shouldn't happen.
738             return;
739         }
740         serializer.startTag(null, TAG_SETTING);
741         serializer.attribute(null, ATTR_ID, id);
742         serializer.attribute(null, ATTR_NAME, name);
743         setValueAttribute(ATTR_VALUE, ATTR_VALUE_BASE64,
744                 version, serializer, value);
745         serializer.attribute(null, ATTR_PACKAGE, packageName);
746         if (defaultValue != null) {
747             setValueAttribute(ATTR_DEFAULT_VALUE, ATTR_DEFAULT_VALUE_BASE64,
748                     version, serializer, defaultValue);
749             serializer.attribute(null, ATTR_DEFAULT_SYS_SET, Boolean.toString(defaultSysSet));
750             setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64,
751                     version, serializer, tag);
752         }
753         serializer.endTag(null, TAG_SETTING);
754     }
755 
setValueAttribute(String attr, String attrBase64, int version, XmlSerializer serializer, String value)756     static void setValueAttribute(String attr, String attrBase64, int version,
757             XmlSerializer serializer, String value) throws IOException {
758         if (version >= SETTINGS_VERSION_NEW_ENCODING) {
759             if (value == null) {
760                 // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64.
761             } else if (isBinary(value)) {
762                 serializer.attribute(null, attrBase64, base64Encode(value));
763             } else {
764                 serializer.attribute(null, attr, value);
765             }
766         } else {
767             // Old encoding.
768             if (value == null) {
769                 serializer.attribute(null, attr, NULL_VALUE_OLD_STYLE);
770             } else {
771                 serializer.attribute(null, attr, value);
772             }
773         }
774     }
775 
getValueAttribute(XmlPullParser parser, String attr, String base64Attr)776     private String getValueAttribute(XmlPullParser parser, String attr, String base64Attr) {
777         if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) {
778             final String value = parser.getAttributeValue(null, attr);
779             if (value != null) {
780                 return value;
781             }
782             final String base64 = parser.getAttributeValue(null, base64Attr);
783             if (base64 != null) {
784                 return base64Decode(base64);
785             }
786             // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64.
787             return null;
788         } else {
789             // Old encoding.
790             final String stored = parser.getAttributeValue(null, attr);
791             if (NULL_VALUE_OLD_STYLE.equals(stored)) {
792                 return null;
793             } else {
794                 return stored;
795             }
796         }
797     }
798 
799     @GuardedBy("mLock")
readStateSyncLocked()800     private void readStateSyncLocked() {
801         FileInputStream in;
802         try {
803             in = new AtomicFile(mStatePersistFile).openRead();
804         } catch (FileNotFoundException fnfe) {
805             Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
806             addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
807             return;
808         }
809         try {
810             XmlPullParser parser = Xml.newPullParser();
811             parser.setInput(in, StandardCharsets.UTF_8.name());
812             parseStateLocked(parser);
813         } catch (XmlPullParserException | IOException e) {
814             String message = "Failed parsing settings file: " + mStatePersistFile;
815             Slog.wtf(LOG_TAG, message);
816             throw new IllegalStateException(message, e);
817         } finally {
818             IoUtils.closeQuietly(in);
819         }
820     }
821 
822     /**
823      * Uses AtomicFile to check if the file or its backup exists.
824      * @param file The file to check for existence
825      * @return whether the original or backup exist
826      */
stateFileExists(File file)827     public static boolean stateFileExists(File file) {
828         AtomicFile stateFile = new AtomicFile(file);
829         return stateFile.exists();
830     }
831 
parseStateLocked(XmlPullParser parser)832     private void parseStateLocked(XmlPullParser parser)
833             throws IOException, XmlPullParserException {
834         final int outerDepth = parser.getDepth();
835         int type;
836         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
837                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
838             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
839                 continue;
840             }
841 
842             String tagName = parser.getName();
843             if (tagName.equals(TAG_SETTINGS)) {
844                 parseSettingsLocked(parser);
845             }
846         }
847     }
848 
849     @GuardedBy("mLock")
parseSettingsLocked(XmlPullParser parser)850     private void parseSettingsLocked(XmlPullParser parser)
851             throws IOException, XmlPullParserException {
852 
853         mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
854 
855         final int outerDepth = parser.getDepth();
856         int type;
857         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
858                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
859             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
860                 continue;
861             }
862 
863             String tagName = parser.getName();
864             if (tagName.equals(TAG_SETTING)) {
865                 String id = parser.getAttributeValue(null, ATTR_ID);
866                 String name = parser.getAttributeValue(null, ATTR_NAME);
867                 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64);
868                 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
869                 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE,
870                         ATTR_DEFAULT_VALUE_BASE64);
871                 String tag = null;
872                 boolean fromSystem = false;
873                 if (defaultValue != null) {
874                     fromSystem = Boolean.parseBoolean(parser.getAttributeValue(
875                             null, ATTR_DEFAULT_SYS_SET));
876                     tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);
877                 }
878                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
879                         fromSystem, id));
880 
881                 if (DEBUG_PERSISTENCE) {
882                     Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
883                 }
884             }
885         }
886     }
887 
888     private final class MyHandler extends Handler {
889         public static final int MSG_PERSIST_SETTINGS = 1;
890 
MyHandler(Looper looper)891         public MyHandler(Looper looper) {
892             super(looper);
893         }
894 
895         @Override
handleMessage(Message message)896         public void handleMessage(Message message) {
897             switch (message.what) {
898                 case MSG_PERSIST_SETTINGS: {
899                     Runnable callback = (Runnable) message.obj;
900                     doWriteState();
901                     if (callback != null) {
902                         callback.run();
903                     }
904                 }
905                 break;
906             }
907         }
908     }
909 
910     private class HistoricalOperation {
911         final long mTimestamp;
912         final String mOperation;
913         final Setting mSetting;
914 
HistoricalOperation(long timestamp, String operation, Setting setting)915         public HistoricalOperation(long timestamp,
916                 String operation, Setting setting) {
917             mTimestamp = timestamp;
918             mOperation = operation;
919             mSetting = setting;
920         }
921     }
922 
923     class Setting {
924         private String name;
925         private String value;
926         private String defaultValue;
927         private String packageName;
928         private String id;
929         private String tag;
930         // Whether the default is set by the system
931         private boolean defaultFromSystem;
932 
Setting(Setting other)933         public Setting(Setting other) {
934             name = other.name;
935             value = other.value;
936             defaultValue = other.defaultValue;
937             packageName = other.packageName;
938             id = other.id;
939             defaultFromSystem = other.defaultFromSystem;
940             tag = other.tag;
941         }
942 
Setting(String name, String value, boolean makeDefault, String packageName, String tag)943         public Setting(String name, String value, boolean makeDefault, String packageName,
944                 String tag) {
945             this.name = name;
946             update(value, makeDefault, packageName, tag, false);
947         }
948 
Setting(String name, String value, String defaultValue, String packageName, String tag, boolean fromSystem, String id)949         public Setting(String name, String value, String defaultValue,
950                 String packageName, String tag, boolean fromSystem, String id) {
951             mNextId = Math.max(mNextId, Long.parseLong(id) + 1);
952             if (NULL_VALUE.equals(value)) {
953                 value = null;
954             }
955             init(name, value, tag, defaultValue, packageName, fromSystem, id);
956         }
957 
init(String name, String value, String tag, String defaultValue, String packageName, boolean fromSystem, String id)958         private void init(String name, String value, String tag, String defaultValue,
959                 String packageName, boolean fromSystem, String id) {
960             this.name = name;
961             this.value = value;
962             this.tag = tag;
963             this.defaultValue = defaultValue;
964             this.packageName = packageName;
965             this.id = id;
966             this.defaultFromSystem = fromSystem;
967         }
968 
getName()969         public String getName() {
970             return name;
971         }
972 
getKey()973         public int getKey() {
974             return mKey;
975         }
976 
getValue()977         public String getValue() {
978             return value;
979         }
980 
getTag()981         public String getTag() {
982             return tag;
983         }
984 
getDefaultValue()985         public String getDefaultValue() {
986             return defaultValue;
987         }
988 
getPackageName()989         public String getPackageName() {
990             return packageName;
991         }
992 
isDefaultFromSystem()993         public boolean isDefaultFromSystem() {
994             return defaultFromSystem;
995         }
996 
getId()997         public String getId() {
998             return id;
999         }
1000 
isNull()1001         public boolean isNull() {
1002             return false;
1003         }
1004 
1005         /** @return whether the value changed */
reset()1006         public boolean reset() {
1007             return update(this.defaultValue, false, packageName, null, true);
1008         }
1009 
isTransient()1010         public boolean isTransient() {
1011             switch (getTypeFromKey(getKey())) {
1012                 case SETTINGS_TYPE_GLOBAL:
1013                     return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
1014             }
1015             return false;
1016         }
1017 
update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage)1018         public boolean update(String value, boolean setDefault, String packageName, String tag,
1019                 boolean forceNonSystemPackage) {
1020             if (NULL_VALUE.equals(value)) {
1021                 value = null;
1022             }
1023 
1024             final boolean callerSystem = !forceNonSystemPackage &&
1025                     !isNull() && isSystemPackage(mContext, packageName);
1026             // Settings set by the system are always defaults.
1027             if (callerSystem) {
1028                 setDefault = true;
1029             }
1030 
1031             String defaultValue = this.defaultValue;
1032             boolean defaultFromSystem = this.defaultFromSystem;
1033             if (setDefault) {
1034                 if (!Objects.equals(value, this.defaultValue)
1035                         && (!defaultFromSystem || callerSystem)) {
1036                     defaultValue = value;
1037                     // Default null means no default, so the tag is irrelevant
1038                     // since it is used to reset a settings subset their defaults.
1039                     // Also it is irrelevant if the system set the canonical default.
1040                     if (defaultValue == null) {
1041                         tag = null;
1042                         defaultFromSystem = false;
1043                     }
1044                 }
1045                 if (!defaultFromSystem && value != null) {
1046                     if (callerSystem) {
1047                         defaultFromSystem = true;
1048                     }
1049                 }
1050             }
1051 
1052             // Is something gonna change?
1053             if (Objects.equals(value, this.value)
1054                     && Objects.equals(defaultValue, this.defaultValue)
1055                     && Objects.equals(packageName, this.packageName)
1056                     && Objects.equals(tag, this.tag)
1057                     && defaultFromSystem == this.defaultFromSystem) {
1058                 return false;
1059             }
1060 
1061             init(name, value, tag, defaultValue, packageName, defaultFromSystem,
1062                     String.valueOf(mNextId++));
1063             return true;
1064         }
1065 
toString()1066         public String toString() {
1067             return "Setting{name=" + name + " value=" + value
1068                     + (defaultValue != null ? " default=" + defaultValue : "")
1069                     + " packageName=" + packageName + " tag=" + tag
1070                     + " defaultFromSystem=" + defaultFromSystem + "}";
1071         }
1072     }
1073 
1074     /**
1075      * @return TRUE if a string is considered "binary" from KXML's point of view.  NOTE DO NOT
1076      * pass null.
1077      */
isBinary(String s)1078     public static boolean isBinary(String s) {
1079         if (s == null) {
1080             throw new NullPointerException();
1081         }
1082         // See KXmlSerializer.writeEscaped
1083         for (int i = 0; i < s.length(); i++) {
1084             char c = s.charAt(i);
1085             boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
1086             if (!allowedInXml) {
1087                 return true;
1088             }
1089         }
1090         return false;
1091     }
1092 
base64Encode(String s)1093     private static String base64Encode(String s) {
1094         return Base64.encodeToString(toBytes(s), Base64.NO_WRAP);
1095     }
1096 
base64Decode(String s)1097     private static String base64Decode(String s) {
1098         return fromBytes(Base64.decode(s, Base64.DEFAULT));
1099     }
1100 
1101     // Note the followings are basically just UTF-16 encode/decode.  But we want to preserve
1102     // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves,
1103     // since I don't know how Charset would treat them.
1104 
toBytes(String s)1105     private static byte[] toBytes(String s) {
1106         final byte[] result = new byte[s.length() * 2];
1107         int resultIndex = 0;
1108         for (int i = 0; i < s.length(); ++i) {
1109             char ch = s.charAt(i);
1110             result[resultIndex++] = (byte) (ch >> 8);
1111             result[resultIndex++] = (byte) ch;
1112         }
1113         return result;
1114     }
1115 
fromBytes(byte[] bytes)1116     private static String fromBytes(byte[] bytes) {
1117         final StringBuffer sb = new StringBuffer(bytes.length / 2);
1118 
1119         final int last = bytes.length - 1;
1120 
1121         for (int i = 0; i < last; i += 2) {
1122             final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff));
1123             sb.append(ch);
1124         }
1125         return sb.toString();
1126     }
1127 
1128     // Check if a specific package belonging to the caller is part of the system package.
isSystemPackage(Context context, String packageName)1129     public static boolean isSystemPackage(Context context, String packageName) {
1130         final int callingUid = Binder.getCallingUid();
1131         final int callingUserId = UserHandle.getUserId(callingUid);
1132         return isSystemPackage(context, packageName, callingUid, callingUserId);
1133     }
1134 
1135     // Check if a specific package, uid, and user ID are part of the system package.
isSystemPackage(Context context, String packageName, int uid, int userId)1136     public static boolean isSystemPackage(Context context, String packageName, int uid,
1137             int userId) {
1138         synchronized (sLock) {
1139             if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
1140                 return true;
1141             }
1142 
1143             // Shell and Root are not considered a part of the system
1144             if (SHELL_PACKAGE_NAME.equals(packageName)
1145                     || ROOT_PACKAGE_NAME.equals(packageName)) {
1146                 return false;
1147             }
1148 
1149             if (uid != INVALID_UID) {
1150                 // Native services running as a special UID get a pass
1151                 final int callingAppId = UserHandle.getAppId(uid);
1152                 if (callingAppId < FIRST_APPLICATION_UID) {
1153                     sSystemUids.put(callingAppId, callingAppId);
1154                     return true;
1155                 }
1156             }
1157 
1158             final long identity = Binder.clearCallingIdentity();
1159             try {
1160                 try {
1161                     uid = context.getPackageManager().getPackageUidAsUser(packageName, 0, userId);
1162                 } catch (PackageManager.NameNotFoundException e) {
1163                     return false;
1164                 }
1165 
1166                 // If the system or a special system UID (like telephony), done.
1167                 if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) {
1168                     sSystemUids.put(uid, uid);
1169                     return true;
1170                 }
1171 
1172                 // If already known system component, done.
1173                 if (sSystemUids.indexOfKey(uid) >= 0) {
1174                     return true;
1175                 }
1176 
1177                 // If SetupWizard, done.
1178                 PackageManagerInternal packageManagerInternal = LocalServices.getService(
1179                         PackageManagerInternal.class);
1180                 if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) {
1181                     sSystemUids.put(uid, uid);
1182                     return true;
1183                 }
1184 
1185                 // If a persistent system app, done.
1186                 PackageInfo packageInfo;
1187                 try {
1188                     packageInfo = context.getPackageManager().getPackageInfoAsUser(
1189                             packageName, PackageManager.GET_SIGNATURES, userId);
1190                     if ((packageInfo.applicationInfo.flags
1191                             & ApplicationInfo.FLAG_PERSISTENT) != 0
1192                             && (packageInfo.applicationInfo.flags
1193                             & ApplicationInfo.FLAG_SYSTEM) != 0) {
1194                         sSystemUids.put(uid, uid);
1195                         return true;
1196                     }
1197                 } catch (PackageManager.NameNotFoundException e) {
1198                     return false;
1199                 }
1200 
1201                 // Last check if system signed.
1202                 if (sSystemSignature == null) {
1203                     try {
1204                         sSystemSignature = context.getPackageManager().getPackageInfoAsUser(
1205                                 SYSTEM_PACKAGE_NAME, PackageManager.GET_SIGNATURES,
1206                                 UserHandle.USER_SYSTEM).signatures[0];
1207                     } catch (PackageManager.NameNotFoundException e) {
1208                         /* impossible */
1209                         return false;
1210                     }
1211                 }
1212                 if (sSystemSignature.equals(packageInfo.signatures[0])) {
1213                     sSystemUids.put(uid, uid);
1214                     return true;
1215                 }
1216             } finally {
1217                 Binder.restoreCallingIdentity(identity);
1218             }
1219 
1220             return false;
1221         }
1222     }
1223 }
1224