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