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