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