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 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
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.
225    public int getVersionLocked() {
226        return mVersion;
227    }
228
229    public Setting getNullSetting() {
230        return mNullSetting;
231    }
232
233    // The settings provider must hold its lock when calling here.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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     */
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
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
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
530    private boolean hasSettingLocked(String name) {
531        return mSettings.indexOfKey(name) >= 0;
532    }
533
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
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
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
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
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
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
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
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
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
792        public MyHandler(Looper looper) {
793            super(looper);
794        }
795
796        @Override
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
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
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
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
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
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
870        public String getName() {
871            return name;
872        }
873
874        public int getKey() {
875            return mKey;
876        }
877
878        public String getValue() {
879            return value;
880        }
881
882        public String getTag() {
883            return tag;
884        }
885
886        public String getDefaultValue() {
887            return defaultValue;
888        }
889
890        public String getPackageName() {
891            return packageName;
892        }
893
894        public boolean isDefaultFromSystem() {
895            return defaultFromSystem;
896        }
897
898        public String getId() {
899            return id;
900        }
901
902        public boolean isNull() {
903            return false;
904        }
905
906        /** @return whether the value changed */
907        public boolean reset() {
908            return update(this.defaultValue, false, packageName, null, true);
909        }
910
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
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     */
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
986    private static String base64Encode(String s) {
987        return Base64.encodeToString(toBytes(s), Base64.NO_WRAP);
988    }
989
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
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
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
1021    public static boolean isSystemPackage(Context context, String packageName) {
1022        return isSystemPackage(context, packageName, Binder.getCallingUid());
1023    }
1024
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