• 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         if (!mStatePersistFile.exists()) {
709             Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
710             addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
711             return;
712         }
713         try {
714             in = new AtomicFile(mStatePersistFile).openRead();
715         } catch (FileNotFoundException fnfe) {
716             String message = "No settings state " + mStatePersistFile;
717             Slog.wtf(LOG_TAG, message);
718             Slog.i(LOG_TAG, message);
719             return;
720         }
721         try {
722             XmlPullParser parser = Xml.newPullParser();
723             parser.setInput(in, StandardCharsets.UTF_8.name());
724             parseStateLocked(parser);
725         } catch (XmlPullParserException | IOException e) {
726             String message = "Failed parsing settings file: " + mStatePersistFile;
727             Slog.wtf(LOG_TAG, message);
728             throw new IllegalStateException(message, e);
729         } finally {
730             IoUtils.closeQuietly(in);
731         }
732     }
733 
parseStateLocked(XmlPullParser parser)734     private void parseStateLocked(XmlPullParser parser)
735             throws IOException, XmlPullParserException {
736         final int outerDepth = parser.getDepth();
737         int type;
738         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
739                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
740             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
741                 continue;
742             }
743 
744             String tagName = parser.getName();
745             if (tagName.equals(TAG_SETTINGS)) {
746                 parseSettingsLocked(parser);
747             }
748         }
749     }
750 
parseSettingsLocked(XmlPullParser parser)751     private void parseSettingsLocked(XmlPullParser parser)
752             throws IOException, XmlPullParserException {
753 
754         mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
755 
756         final int outerDepth = parser.getDepth();
757         int type;
758         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
759                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
760             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
761                 continue;
762             }
763 
764             String tagName = parser.getName();
765             if (tagName.equals(TAG_SETTING)) {
766                 String id = parser.getAttributeValue(null, ATTR_ID);
767                 String name = parser.getAttributeValue(null, ATTR_NAME);
768                 String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64);
769                 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
770                 String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE,
771                         ATTR_DEFAULT_VALUE_BASE64);
772                 String tag = null;
773                 boolean fromSystem = false;
774                 if (defaultValue != null) {
775                     fromSystem = Boolean.parseBoolean(parser.getAttributeValue(
776                             null, ATTR_DEFAULT_SYS_SET));
777                     tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);
778                 }
779                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
780                         fromSystem, id));
781 
782                 if (DEBUG_PERSISTENCE) {
783                     Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
784                 }
785             }
786         }
787     }
788 
789     private final class MyHandler extends Handler {
790         public static final int MSG_PERSIST_SETTINGS = 1;
791 
MyHandler(Looper looper)792         public MyHandler(Looper looper) {
793             super(looper);
794         }
795 
796         @Override
handleMessage(Message message)797         public void handleMessage(Message message) {
798             switch (message.what) {
799                 case MSG_PERSIST_SETTINGS: {
800                     Runnable callback = (Runnable) message.obj;
801                     doWriteState();
802                     if (callback != null) {
803                         callback.run();
804                     }
805                 }
806                 break;
807             }
808         }
809     }
810 
811     private class HistoricalOperation {
812         final long mTimestamp;
813         final String mOperation;
814         final Setting mSetting;
815 
HistoricalOperation(long timestamp, String operation, Setting setting)816         public HistoricalOperation(long timestamp,
817                 String operation, Setting setting) {
818             mTimestamp = timestamp;
819             mOperation = operation;
820             mSetting = setting;
821         }
822     }
823 
824     class Setting {
825         private String name;
826         private String value;
827         private String defaultValue;
828         private String packageName;
829         private String id;
830         private String tag;
831         // Whether the default is set by the system
832         private boolean defaultFromSystem;
833 
Setting(Setting other)834         public Setting(Setting other) {
835             name = other.name;
836             value = other.value;
837             defaultValue = other.defaultValue;
838             packageName = other.packageName;
839             id = other.id;
840             defaultFromSystem = other.defaultFromSystem;
841             tag = other.tag;
842         }
843 
Setting(String name, String value, boolean makeDefault, String packageName, String tag)844         public Setting(String name, String value, boolean makeDefault, String packageName,
845                 String tag) {
846             this.name = name;
847             update(value, makeDefault, packageName, tag, false);
848         }
849 
Setting(String name, String value, String defaultValue, String packageName, String tag, boolean fromSystem, String id)850         public Setting(String name, String value, String defaultValue,
851                 String packageName, String tag, boolean fromSystem, String id) {
852             mNextId = Math.max(mNextId, Long.parseLong(id) + 1);
853             if (NULL_VALUE.equals(value)) {
854                 value = null;
855             }
856             init(name, value, tag, defaultValue, packageName, fromSystem, id);
857         }
858 
init(String name, String value, String tag, String defaultValue, String packageName, boolean fromSystem, String id)859         private void init(String name, String value, String tag, String defaultValue,
860                 String packageName, boolean fromSystem, String id) {
861             this.name = name;
862             this.value = value;
863             this.tag = tag;
864             this.defaultValue = defaultValue;
865             this.packageName = packageName;
866             this.id = id;
867             this.defaultFromSystem = fromSystem;
868         }
869 
getName()870         public String getName() {
871             return name;
872         }
873 
getKey()874         public int getKey() {
875             return mKey;
876         }
877 
getValue()878         public String getValue() {
879             return value;
880         }
881 
getTag()882         public String getTag() {
883             return tag;
884         }
885 
getDefaultValue()886         public String getDefaultValue() {
887             return defaultValue;
888         }
889 
getPackageName()890         public String getPackageName() {
891             return packageName;
892         }
893 
isDefaultFromSystem()894         public boolean isDefaultFromSystem() {
895             return defaultFromSystem;
896         }
897 
getId()898         public String getId() {
899             return id;
900         }
901 
isNull()902         public boolean isNull() {
903             return false;
904         }
905 
906         /** @return whether the value changed */
reset()907         public boolean reset() {
908             return update(this.defaultValue, false, packageName, null, true);
909         }
910 
update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage)911         public boolean update(String value, boolean setDefault, String packageName, String tag,
912                 boolean forceNonSystemPackage) {
913             if (NULL_VALUE.equals(value)) {
914                 value = null;
915             }
916 
917             final boolean callerSystem = !forceNonSystemPackage &&
918                     !isNull() && isSystemPackage(mContext, packageName);
919             // Settings set by the system are always defaults.
920             if (callerSystem) {
921                 setDefault = true;
922             }
923 
924             String defaultValue = this.defaultValue;
925             boolean defaultFromSystem = this.defaultFromSystem;
926             if (setDefault) {
927                 if (!Objects.equal(value, this.defaultValue)
928                         && (!defaultFromSystem || callerSystem)) {
929                     defaultValue = value;
930                     // Default null means no default, so the tag is irrelevant
931                     // since it is used to reset a settings subset their defaults.
932                     // Also it is irrelevant if the system set the canonical default.
933                     if (defaultValue == null) {
934                         tag = null;
935                         defaultFromSystem = false;
936                     }
937                 }
938                 if (!defaultFromSystem && value != null) {
939                     if (callerSystem) {
940                         defaultFromSystem = true;
941                     }
942                 }
943             }
944 
945             // Is something gonna change?
946             if (Objects.equal(value, this.value)
947                     && Objects.equal(defaultValue, this.defaultValue)
948                     && Objects.equal(packageName, this.packageName)
949                     && Objects.equal(tag, this.tag)
950                     && defaultFromSystem == this.defaultFromSystem) {
951                 return false;
952             }
953 
954             init(name, value, tag, defaultValue, packageName, defaultFromSystem,
955                     String.valueOf(mNextId++));
956             return true;
957         }
958 
toString()959         public String toString() {
960             return "Setting{name=" + name + " value=" + value
961                     + (defaultValue != null ? " default=" + defaultValue : "")
962                     + " packageName=" + packageName + " tag=" + tag
963                     + " defaultFromSystem=" + defaultFromSystem + "}";
964         }
965     }
966 
967     /**
968      * @return TRUE if a string is considered "binary" from KXML's point of view.  NOTE DO NOT
969      * pass null.
970      */
isBinary(String s)971     public static boolean isBinary(String s) {
972         if (s == null) {
973             throw new NullPointerException();
974         }
975         // See KXmlSerializer.writeEscaped
976         for (int i = 0; i < s.length(); i++) {
977             char c = s.charAt(i);
978             boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
979             if (!allowedInXml) {
980                 return true;
981             }
982         }
983         return false;
984     }
985 
base64Encode(String s)986     private static String base64Encode(String s) {
987         return Base64.encodeToString(toBytes(s), Base64.NO_WRAP);
988     }
989 
base64Decode(String s)990     private static String base64Decode(String s) {
991         return fromBytes(Base64.decode(s, Base64.DEFAULT));
992     }
993 
994     // Note the followings are basically just UTF-16 encode/decode.  But we want to preserve
995     // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves,
996     // since I don't know how Charset would treat them.
997 
toBytes(String s)998     private static byte[] toBytes(String s) {
999         final byte[] result = new byte[s.length() * 2];
1000         int resultIndex = 0;
1001         for (int i = 0; i < s.length(); ++i) {
1002             char ch = s.charAt(i);
1003             result[resultIndex++] = (byte) (ch >> 8);
1004             result[resultIndex++] = (byte) ch;
1005         }
1006         return result;
1007     }
1008 
fromBytes(byte[] bytes)1009     private static String fromBytes(byte[] bytes) {
1010         final StringBuffer sb = new StringBuffer(bytes.length / 2);
1011 
1012         final int last = bytes.length - 1;
1013 
1014         for (int i = 0; i < last; i += 2) {
1015             final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff));
1016             sb.append(ch);
1017         }
1018         return sb.toString();
1019     }
1020 
isSystemPackage(Context context, String packageName)1021     public static boolean isSystemPackage(Context context, String packageName) {
1022         return isSystemPackage(context, packageName, Binder.getCallingUid());
1023     }
1024 
isSystemPackage(Context context, String packageName, int callingUid)1025     public static boolean isSystemPackage(Context context, String packageName, int callingUid) {
1026         synchronized (sLock) {
1027             if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
1028                 return true;
1029             }
1030 
1031             // Shell and Root are not considered a part of the system
1032             if (SHELL_PACKAGE_NAME.equals(packageName)
1033                     || ROOT_PACKAGE_NAME.equals(packageName)) {
1034                 return false;
1035             }
1036 
1037             // Native services running as a special UID get a pass
1038             final int callingAppId = UserHandle.getAppId(callingUid);
1039             if (callingAppId < FIRST_APPLICATION_UID) {
1040                 sSystemUids.put(callingAppId, callingAppId);
1041                 return true;
1042             }
1043 
1044             // While some callers may have permissions to manipulate cross user
1045             // settings or some settings are stored in the parent of a managed
1046             // profile for the purpose of determining whether the other end is a
1047             // system component we need to use the user id of the caller for
1048             // pulling information about the caller from the package manager.
1049             final int callingUserId = UserHandle.getUserId(callingUid);
1050 
1051             final long identity = Binder.clearCallingIdentity();
1052             try {
1053                 final int uid;
1054                 try {
1055                     uid = context.getPackageManager().getPackageUidAsUser(packageName, 0,
1056                             callingUserId);
1057                 } catch (PackageManager.NameNotFoundException e) {
1058                     return false;
1059                 }
1060 
1061                 // If the system or a special system UID (like telephony), done.
1062                 if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) {
1063                     sSystemUids.put(uid, uid);
1064                     return true;
1065                 }
1066 
1067                 // If already known system component, done.
1068                 if (sSystemUids.indexOfKey(uid) >= 0) {
1069                     return true;
1070                 }
1071 
1072                 // If SetupWizard, done.
1073                 PackageManagerInternal packageManagerInternal = LocalServices.getService(
1074                         PackageManagerInternal.class);
1075                 if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) {
1076                     sSystemUids.put(uid, uid);
1077                     return true;
1078                 }
1079 
1080                 // If a persistent system app, done.
1081                 PackageInfo packageInfo;
1082                 try {
1083                     packageInfo = context.getPackageManager().getPackageInfoAsUser(
1084                             packageName, PackageManager.GET_SIGNATURES, callingUserId);
1085                     if ((packageInfo.applicationInfo.flags
1086                             & ApplicationInfo.FLAG_PERSISTENT) != 0
1087                             && (packageInfo.applicationInfo.flags
1088                             & ApplicationInfo.FLAG_SYSTEM) != 0) {
1089                         sSystemUids.put(uid, uid);
1090                         return true;
1091                     }
1092                 } catch (PackageManager.NameNotFoundException e) {
1093                     return false;
1094                 }
1095 
1096                 // Last check if system signed.
1097                 if (sSystemSignature == null) {
1098                     try {
1099                         sSystemSignature = context.getPackageManager().getPackageInfoAsUser(
1100                                 SYSTEM_PACKAGE_NAME, PackageManager.GET_SIGNATURES,
1101                                 UserHandle.USER_SYSTEM).signatures[0];
1102                     } catch (PackageManager.NameNotFoundException e) {
1103                         /* impossible */
1104                         return false;
1105                     }
1106                 }
1107                 if (sSystemSignature.equals(packageInfo.signatures[0])) {
1108                     sSystemUids.put(uid, uid);
1109                     return true;
1110                 }
1111             } finally {
1112                 Binder.restoreCallingIdentity(identity);
1113             }
1114 
1115             return false;
1116         }
1117     }
1118 }
1119