154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project/*
254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *
454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * you may not use this file except in compliance with the License.
654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * You may obtain a copy of the License at
754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *
854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *
1054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * See the License for the specific language governing permissions and
1454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * limitations under the License.
1554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project */
1654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
1754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectpackage com.android.providers.settings;
1854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
19501eec92f9f4f206ad7972c63f2d0ef0285d8e34-b masterimport java.io.FileNotFoundException;
204f8ff39c1e2448d44ac900e04f9348f9d2aeaaf5Doug Zongkerimport java.security.SecureRandom;
2106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport java.util.HashSet;
22f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrickimport java.util.concurrent.atomic.AtomicBoolean;
23f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrickimport java.util.concurrent.atomic.AtomicInteger;
24501eec92f9f4f206ad7972c63f2d0ef0285d8e34-b master
25d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tateimport android.app.ActivityManager;
26961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackbornimport android.app.AppOpsManager;
274528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupManager;
2806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.content.BroadcastReceiver;
2954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.ContentProvider;
3054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.ContentUris;
3154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.ContentValues;
3254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.Context;
3306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.content.Intent;
3406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.content.IntentFilter;
3554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.pm.PackageManager;
3638e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tateimport android.content.pm.UserInfo;
3769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissenimport android.content.res.AssetFileDescriptor;
38afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tateimport android.database.AbstractCursor;
3954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.Cursor;
4054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
411877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrickimport android.database.sqlite.SQLiteException;
4254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
4354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.media.RingtoneManager;
4454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.net.Uri;
4506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.os.Binder;
461877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrickimport android.os.Bundle;
475cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasaniimport android.os.DropBoxManager;
48f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrickimport android.os.FileObserver;
4954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.os.ParcelFileDescriptor;
500da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tateimport android.os.Process;
5154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.os.SystemProperties;
5206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.os.UserHandle;
5306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.os.UserManager;
5454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.provider.MediaStore;
5554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.provider.Settings;
5654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.text.TextUtils;
5754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.util.Log;
580c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilsonimport android.util.LruCache;
5906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.util.Slog;
6006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.util.SparseArray;
6154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
6254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectpublic class SettingsProvider extends ContentProvider {
6354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private static final String TAG = "SettingsProvider";
644dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate    private static final boolean LOCAL_LOGV = false;
6554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
660da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate    private static final boolean USER_CHECK_THROWS = true;
670da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate
6806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final String TABLE_SYSTEM = "system";
6906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final String TABLE_SECURE = "secure";
7006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final String TABLE_GLOBAL = "global";
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TABLE_FAVORITES = "favorites";
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TABLE_OLD_FAVORITES = "old_favorites";
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
741877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    private static final String[] COLUMN_VALUE = new String[] { "value" };
751877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick
7606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Caches for each user's settings, access-ordered for acting as LRU.
771bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    // Guarded by themselves.
78f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    private static final int MAX_CACHE_ENTRIES = 200;
7906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SparseArray<SettingsCache> sSystemCaches
8006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<SettingsCache>();
8106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SparseArray<SettingsCache> sSecureCaches
8206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<SettingsCache>();
8306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SettingsCache sGlobalCache = new SettingsCache(TABLE_GLOBAL);
84f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
85f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // The count of how many known (handled by SettingsProvider)
8606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // database mutations are currently being handled for this user.
8706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Used by file observers to not reload the database when it's ourselves
88f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // modifying it.
8906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SparseArray<AtomicInteger> sKnownMutationsInFlight
9006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<AtomicInteger>();
911bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
9278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    // Each defined user has their own settings
9378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
9478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
95342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    // Over this size we don't reject loading or saving settings but
96342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    // we do consider them broken/malicious and don't keep them in
97342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    // memory at least:
98342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    private static final int MAX_CACHE_ENTRY_SIZE = 500;
99342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick
1001bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    private static final Bundle NULL_SETTING = Bundle.forPair("value", null);
1011bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
102f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // Used as a sentinel value in an instance equality test when we
103f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // want to cache the existence of a key, but not store its value.
104f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
105f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
10606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private UserManager mUserManager;
1078823c0a8c68fe669c21c539eef9fc6541f0c7494Amith Yamasani    private BackupManager mBackupManager;
10854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
10954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
11006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     * Settings which need to be treated as global/shared in multi-user environments.
11106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     */
11206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    static final HashSet<String> sSecureGlobalKeys;
11306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    static final HashSet<String> sSystemGlobalKeys;
1145cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani
1155cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani    private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
1165cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani
11706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    static {
11806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Keys (name column) from the 'secure' table that are now in the owner user's 'global'
11906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // table, shared across all users
12006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // These must match Settings.Secure.MOVED_TO_GLOBAL
12106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sSecureGlobalKeys = new HashSet<String>();
12266488d64df8c3cf8722b8bf282398617cf3c0551Christopher Tate        Settings.Secure.getMovedKeys(sSecureGlobalKeys);
12306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
12406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Keys from the 'system' table now moved to 'global'
12506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // These must match Settings.System.MOVED_TO_GLOBAL
12606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sSystemGlobalKeys = new HashSet<String>();
12766488d64df8c3cf8722b8bf282398617cf3c0551Christopher Tate        Settings.System.getNonLegacyMovedKeys(sSystemGlobalKeys);
12806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
12906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
13006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private boolean settingMovedToGlobal(final String name) {
13106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        return sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name);
13206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
13306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
13406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    /**
13554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Decode a content URL into the table, projection, and arguments
13654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * used to access the corresponding database rows.
13754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
13854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private static class SqlArguments {
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String table;
14054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        public final String where;
14154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        public final String[] args;
14254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
14354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        /** Operate on existing rows. */
14454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments(Uri url, String where, String[] args) {
14554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (url.getPathSegments().size() == 1) {
146c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                // of the form content://settings/secure, arbitrary where clause
14754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.table = url.getPathSegments().get(0);
14824117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                if (!DatabaseHelper.isValidTable(this.table)) {
14924117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                    throw new IllegalArgumentException("Bad root path: " + this.table);
15024117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                }
15154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.where = where;
15254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.args = args;
15354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else if (url.getPathSegments().size() != 2) {
15454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                throw new IllegalArgumentException("Invalid URI: " + url);
15554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else if (!TextUtils.isEmpty(where)) {
15654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                throw new UnsupportedOperationException("WHERE clause not supported: " + url);
15754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
158c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                // of the form content://settings/secure/element_name, no where clause
15954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.table = url.getPathSegments().get(0);
16024117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                if (!DatabaseHelper.isValidTable(this.table)) {
16124117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                    throw new IllegalArgumentException("Bad root path: " + this.table);
16224117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                }
1635bcb55186ebda12d9e4308043898f7aa3ac5c952Doug Zongker                if (TABLE_SYSTEM.equals(this.table) || TABLE_SECURE.equals(this.table) ||
1645bcb55186ebda12d9e4308043898f7aa3ac5c952Doug Zongker                    TABLE_GLOBAL.equals(this.table)) {
16554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    this.where = Settings.NameValueTable.NAME + "=?";
166c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    final String name = url.getPathSegments().get(1);
167c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    this.args = new String[] { name };
168c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    // Rewrite the table for known-migrated names
169c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    if (TABLE_SYSTEM.equals(this.table) || TABLE_SECURE.equals(this.table)) {
170c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                        if (sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name)) {
171c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                            this.table = TABLE_GLOBAL;
172c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                        }
173c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    }
17454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                } else {
175c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    // of the form content://bookmarks/19
17654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    this.where = "_id=" + ContentUris.parseId(url);
17754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    this.args = null;
17854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
17954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
18054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
18154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
18254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        /** Insert new rows (no where clause allowed). */
18354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments(Uri url) {
18454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (url.getPathSegments().size() == 1) {
18554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.table = url.getPathSegments().get(0);
18624117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                if (!DatabaseHelper.isValidTable(this.table)) {
18724117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                    throw new IllegalArgumentException("Bad root path: " + this.table);
18824117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                }
18954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.where = null;
19054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.args = null;
19154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
19254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                throw new IllegalArgumentException("Invalid URI: " + url);
19354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
19454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
19554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
19654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
19754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
19854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Get the content URI of a row added to a table.
19954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param tableUri of the entire table
20054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param values found in the row
20154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param rowId of the row
20254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the content URI for this particular row
20354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
20454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private Uri getUriFor(Uri tableUri, ContentValues values, long rowId) {
20554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (tableUri.getPathSegments().size() != 1) {
20654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            throw new IllegalArgumentException("Invalid URI: " + tableUri);
20754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
20854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String table = tableUri.getPathSegments().get(0);
20906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_SYSTEM.equals(table) ||
21006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                TABLE_SECURE.equals(table) ||
21106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                TABLE_GLOBAL.equals(table)) {
21254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String name = values.getAsString(Settings.NameValueTable.NAME);
21354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return Uri.withAppendedPath(tableUri, name);
21454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
21554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return ContentUris.withAppendedId(tableUri, rowId);
21654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
21754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
21854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
21954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
22054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Send a notification when a particular content URI changes.
22154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Modify the system property used to communicate the version of
22254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * this table, for tables which have such a property.  (The Settings
22354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * contract class uses these to provide client-side caches.)
22454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param uri to send notifications for
22554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
22606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void sendNotify(Uri uri, int userHandle) {
22754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Update the system property *first*, so if someone is listening for
22854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // a notification and then using the contract class to get their data,
22954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // the system property will be updated and they'll get the new data.
23054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
231d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani        boolean backedUpDataChanged = false;
23254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String property = null, table = uri.getPathSegments().get(0);
23316aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate        final boolean isGlobal = table.equals(TABLE_GLOBAL);
23406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (table.equals(TABLE_SYSTEM)) {
235139748fd724b482e2c012a6ec44d1c5abc0c0e97Dianne Hackborn            property = Settings.System.SYS_PROP_SETTING_VERSION;
23606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            backedUpDataChanged = true;
23706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        } else if (table.equals(TABLE_SECURE)) {
238139748fd724b482e2c012a6ec44d1c5abc0c0e97Dianne Hackborn            property = Settings.Secure.SYS_PROP_SETTING_VERSION;
239d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani            backedUpDataChanged = true;
24016aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate        } else if (isGlobal) {
24106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            property = Settings.Global.SYS_PROP_SETTING_VERSION;    // this one is global
242d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani            backedUpDataChanged = true;
24354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
24454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
24554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (property != null) {
24654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            long version = SystemProperties.getLong(property, 0) + 1;
24754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (LOCAL_LOGV) Log.v(TAG, "property: " + property + "=" + version);
24854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            SystemProperties.set(property, Long.toString(version));
24954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
25054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
251501eec92f9f4f206ad7972c63f2d0ef0285d8e34-b master        // Inform the backup manager about a data change
252d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani        if (backedUpDataChanged) {
253d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani            mBackupManager.dataChanged();
254d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani        }
25554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Now send the notification through the content framework.
25654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
25754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String notify = uri.getQueryParameter("notify");
25854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (notify == null || "true".equals(notify)) {
25916aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate            final int notifyTarget = isGlobal ? UserHandle.USER_ALL : userHandle;
260c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            final long oldId = Binder.clearCallingIdentity();
261c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            try {
262c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate                getContext().getContentResolver().notifyChange(uri, null, true, notifyTarget);
263c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            } finally {
264c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate                Binder.restoreCallingIdentity(oldId);
265c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            }
26616aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate            if (LOCAL_LOGV) Log.v(TAG, "notifying for " + notifyTarget + ": " + uri);
26754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
26854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (LOCAL_LOGV) Log.v(TAG, "notification suppressed: " + uri);
26954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
27054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
27154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
27254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
27354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Make sure the caller has permission to write this data.
27454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param args supplied by the caller
27554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @throws SecurityException if the caller is forbidden to write.
27654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
27754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private void checkWritePermissions(SqlArguments args) {
27806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if ((TABLE_SECURE.equals(args.table) || TABLE_GLOBAL.equals(args.table)) &&
279f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project            getContext().checkCallingOrSelfPermission(
280aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker                    android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
281aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker            PackageManager.PERMISSION_GRANTED) {
28216dd82cfdc879b7c3e51b19e54c70dbf78e8d697Brett Chabot            throw new SecurityException(
283aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker                    String.format("Permission denial: writing to secure settings requires %1$s",
284aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker                                  android.Manifest.permission.WRITE_SECURE_SETTINGS));
28554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
28654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
28754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
288f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // FileObserver for external modifications to the database file.
289f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // Note that this is for platform developers only with
290f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // userdebug/eng builds who should be able to tinker with the
291f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // sqlite database out from under the SettingsProvider, which is
292f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // normally the exclusive owner of the database.  But we keep this
293f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // enabled all the time to minimize development-vs-user
294f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // differences in testing.
29506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static SparseArray<SettingsFileObserver> sObserverInstances
29606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<SettingsFileObserver>();
297f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    private class SettingsFileObserver extends FileObserver {
298f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
29906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        private final int mUserHandle;
300f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private final String mPath;
301f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
30206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        public SettingsFileObserver(int userHandle, String path) {
303f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            super(path, FileObserver.CLOSE_WRITE |
304f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                  FileObserver.CREATE | FileObserver.DELETE |
305f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                  FileObserver.MOVED_TO | FileObserver.MODIFY);
30606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mUserHandle = userHandle;
307f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            mPath = path;
308f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
309f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
310f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public void onEvent(int event, String path) {
31106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            int modsInFlight = sKnownMutationsInFlight.get(mUserHandle).get();
312f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            if (modsInFlight > 0) {
313f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // our own modification.
314f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return;
315f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
31606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Log.d(TAG, "User " + mUserHandle + " external modification to " + mPath
31706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    + "; event=" + event);
318f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            if (!mIsDirty.compareAndSet(false, true)) {
319f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // already handled. (we get a few update events
320f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // during an sqlite write)
321f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return;
322f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
32306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Log.d(TAG, "User " + mUserHandle + " updating our caches for " + mPath);
32406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            fullyPopulateCaches(mUserHandle);
325f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            mIsDirty.set(false);
326f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
327f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
328f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
32954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
33054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public boolean onCreate() {
3318823c0a8c68fe669c21c539eef9fc6541f0c7494Amith Yamasani        mBackupManager = new BackupManager(getContext());
33227db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani        mUserManager = UserManager.get(getContext());
33306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
334961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        setAppOps(AppOpsManager.OP_NONE, AppOpsManager.OP_WRITE_SETTINGS);
33578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        establishDbTracking(UserHandle.USER_OWNER);
33678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
33778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        IntentFilter userFilter = new IntentFilter();
33878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        userFilter.addAction(Intent.ACTION_USER_REMOVED);
33978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        getContext().registerReceiver(new BroadcastReceiver() {
34078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            @Override
34178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            public void onReceive(Context context, Intent intent) {
34278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
34378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
34478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                            UserHandle.USER_OWNER);
34578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                    if (userHandle != UserHandle.USER_OWNER) {
34678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                        onUserRemoved(userHandle);
34706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    }
34806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                }
34978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            }
35078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        }, userFilter);
35154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return true;
35254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
35354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
35406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    void onUserRemoved(int userHandle) {
35506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        synchronized (this) {
35678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            // the db file itself will be deleted automatically, but we need to tear down
35778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            // our caches and other internal bookkeeping.
35806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            FileObserver observer = sObserverInstances.get(userHandle);
35906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            if (observer != null) {
36006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                observer.stopWatching();
36106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                sObserverInstances.delete(userHandle);
362f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
36306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
36406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mOpenHelpers.delete(userHandle);
36506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sSystemCaches.delete(userHandle);
36606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sSecureCaches.delete(userHandle);
36706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sKnownMutationsInFlight.delete(userHandle);
36806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
36906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
37006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
37178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    private void establishDbTracking(int userHandle) {
37206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) {
37306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Slog.i(TAG, "Installing settings db helper and caches for user " + userHandle);
37406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
37506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
37678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbhelper;
37706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
37878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        synchronized (this) {
37978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            dbhelper = mOpenHelpers.get(userHandle);
38078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            if (dbhelper == null) {
38178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                dbhelper = new DatabaseHelper(getContext(), userHandle);
38278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                mOpenHelpers.append(userHandle, dbhelper);
38378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
38478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sSystemCaches.append(userHandle, new SettingsCache(TABLE_SYSTEM));
38578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sSecureCaches.append(userHandle, new SettingsCache(TABLE_SECURE));
38678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sKnownMutationsInFlight.append(userHandle, new AtomicInteger(0));
38778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            }
38878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        }
38978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
39078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // Initialization of the db *outside* the locks.  It's possible that racing
39178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // threads might wind up here, the second having read the cache entries
39278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // written by the first, but that's benign: the SQLite helper implementation
39378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // manages concurrency itself, and it's important that we not run the db
39478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // initialization with any of our own locks held, so we're fine.
39506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbhelper.getWritableDatabase();
39606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
39778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // Watch for external modifications to the database files,
39878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // keeping our caches in sync.  We synchronize the observer set
39978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // separately, and of course it has to run after the db file
40078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // itself was set up by the DatabaseHelper.
40178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        synchronized (sObserverInstances) {
40278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            if (sObserverInstances.get(userHandle) == null) {
40378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                SettingsFileObserver observer = new SettingsFileObserver(userHandle, db.getPath());
40478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sObserverInstances.append(userHandle, observer);
40578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                observer.startWatching();
40678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            }
40778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        }
40806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
4094dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        ensureAndroidIdIsSet(userHandle);
4104dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate
41106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        startAsyncCachePopulation(userHandle);
41206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
41306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
41406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    class CachePrefetchThread extends Thread {
41506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        private int mUserHandle;
41606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
41706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        CachePrefetchThread(int userHandle) {
41806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            super("populate-settings-caches");
41906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mUserHandle = userHandle;
42006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
42106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
42206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        @Override
42306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        public void run() {
42406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            fullyPopulateCaches(mUserHandle);
42506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
426f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
427f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
42806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void startAsyncCachePopulation(int userHandle) {
42906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        new CachePrefetchThread(userHandle).start();
43006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
43106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
43206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void fullyPopulateCaches(final int userHandle) {
43306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        DatabaseHelper dbHelper = mOpenHelpers.get(userHandle);
43406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Only populate the globals cache once, for the owning user
43506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (userHandle == UserHandle.USER_OWNER) {
43606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            fullyPopulateCache(dbHelper, TABLE_GLOBAL, sGlobalCache);
43706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
43806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        fullyPopulateCache(dbHelper, TABLE_SECURE, sSecureCaches.get(userHandle));
43906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        fullyPopulateCache(dbHelper, TABLE_SYSTEM, sSystemCaches.get(userHandle));
440f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
441f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
442f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // Slurp all values (if sane in number & size) into cache.
44306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void fullyPopulateCache(DatabaseHelper dbHelper, String table, SettingsCache cache) {
44406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbHelper.getReadableDatabase();
445f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        Cursor c = db.query(
446f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            table,
447f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
448f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            null, null, null, null, null,
449f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            "" + (MAX_CACHE_ENTRIES + 1) /* limit */);
450f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        try {
451f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (cache) {
4520c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson                cache.evictAll();
453f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                cache.setFullyMatchesDisk(true);  // optimistic
454f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                int rows = 0;
455f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                while (c.moveToNext()) {
456f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    rows++;
457f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    String name = c.getString(0);
458f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    String value = c.getString(1);
459f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    cache.populate(name, value);
460f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                }
461f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                if (rows > MAX_CACHE_ENTRIES) {
462f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    // Somewhat redundant, as removeEldestEntry() will
463f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    // have already done this, but to be explicit:
464f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    cache.setFullyMatchesDisk(false);
465f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    Log.d(TAG, "row count exceeds max cache entries for table " + table);
466f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                }
46740e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                if (LOCAL_LOGV) Log.d(TAG, "cache for settings table '" + table
46840e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        + "' rows=" + rows + "; fullycached=" + cache.fullyMatchesDisk());
469f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
470f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        } finally {
471f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            c.close();
472f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
473f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
474f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
4754dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate    private boolean ensureAndroidIdIsSet(int userHandle) {
4764dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        final Cursor c = queryForUser(Settings.Secure.CONTENT_URI,
477c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                new String[] { Settings.NameValueTable.VALUE },
478c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                Settings.NameValueTable.NAME + "=?",
4794dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                new String[] { Settings.Secure.ANDROID_ID }, null,
4804dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                userHandle);
481c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana        try {
482c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            final String value = c.moveToNext() ? c.getString(0) : null;
483c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            if (value == null) {
48438e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                // sanity-check the user before touching the db
48538e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                final UserInfo user = mUserManager.getUserInfo(userHandle);
48638e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                if (user == null) {
48738e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                    // can happen due to races when deleting users; treat as benign
48838e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                    return false;
48938e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                }
49038e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate
4919bb4ec484b9b9518bf5b17484dcb50727c58b5d1Nick Kralevich                final SecureRandom random = new SecureRandom();
492c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                final String newAndroidIdValue = Long.toHexString(random.nextLong());
493c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                final ContentValues values = new ContentValues();
494c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
495c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
4964dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                final Uri uri = insertForUser(Settings.Secure.CONTENT_URI, values, userHandle);
497c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                if (uri == null) {
4984dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                    Slog.e(TAG, "Unable to generate new ANDROID_ID for user " + userHandle);
499c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                    return false;
500c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                }
5014dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                Slog.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue
5024dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                        + "] for user " + userHandle);
5035cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                // Write a dropbox entry if it's a restricted profile
50438e7a60fd7fecdf1c6593724111a92147b4c50ffChristopher Tate                if (user.isRestricted()) {
5055cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                    DropBoxManager dbm = (DropBoxManager)
5065cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                            getContext().getSystemService(Context.DROPBOX_SERVICE);
5075cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                    if (dbm != null && dbm.isTagEnabled(DROPBOX_TAG_USERLOG)) {
5085cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                        dbm.addText(DROPBOX_TAG_USERLOG, System.currentTimeMillis()
5095cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                                + ",restricted_profile_ssaid,"
5105cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                                + newAndroidIdValue + "\n");
5115cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                    }
5125cdf7f5b2a75455e8024a4cb892ac38bcd8e9582Amith Yamasani                }
513c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            }
514c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            return true;
515c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana        } finally {
516c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            c.close();
517c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana        }
518c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana    }
519c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana
52006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Lazy-initialize the settings caches for non-primary users
52106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private SettingsCache getOrConstructCache(int callingUser, SparseArray<SettingsCache> which) {
52278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        getOrEstablishDatabase(callingUser); // ignore return value; we don't need it
52378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        return which.get(callingUser);
52406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
52506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
52606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Lazy initialize the database helper and caches for this user, if necessary
52778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    private DatabaseHelper getOrEstablishDatabase(int callingUser) {
5280da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate        if (callingUser >= Process.SYSTEM_UID) {
5290da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate            if (USER_CHECK_THROWS) {
5300da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate                throw new IllegalArgumentException("Uid rather than user handle: " + callingUser);
5310da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate            } else {
5320da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate                Slog.wtf(TAG, "establish db for uid rather than user: " + callingUser);
5330da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate            }
5340da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate        }
5350da1357f9827b6a2df24a449dbb8eba12484095cChristopher Tate
53606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        long oldId = Binder.clearCallingIdentity();
53706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        try {
53806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            DatabaseHelper dbHelper = mOpenHelpers.get(callingUser);
53906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            if (null == dbHelper) {
54078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                establishDbTracking(callingUser);
54106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                dbHelper = mOpenHelpers.get(callingUser);
54206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            }
54306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return dbHelper;
54406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        } finally {
54506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Binder.restoreCallingIdentity(oldId);
54606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
54706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
54806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
54906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    public SettingsCache cacheForTable(final int callingUser, String tableName) {
55006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_SYSTEM.equals(tableName)) {
55106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return getOrConstructCache(callingUser, sSystemCaches);
55206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
55306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_SECURE.equals(tableName)) {
55406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return getOrConstructCache(callingUser, sSecureCaches);
55506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
55606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_GLOBAL.equals(tableName)) {
55706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return sGlobalCache;
55806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
55906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        return null;
56006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
56106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
56206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    /**
56306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     * Used for wiping a whole cache on deletes when we're not
56406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     * sure what exactly was deleted or changed.
56506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     */
56606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    public void invalidateCache(final int callingUser, String tableName) {
56706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SettingsCache cache = cacheForTable(callingUser, tableName);
56806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (cache == null) {
56906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return;
57006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
57106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        synchronized (cache) {
57206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            cache.evictAll();
57306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            cache.mCacheFullyMatchesDisk = false;
57406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
57506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
57606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
5771877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    /**
5781877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick     * Fast path that avoids the use of chatty remoted Cursors.
5791877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick     */
5801877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    @Override
581911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey    public Bundle call(String method, String request, Bundle args) {
58206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        int callingUser = UserHandle.getCallingUserId();
58306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (args != null) {
58406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            int reqUser = args.getInt(Settings.CALL_METHOD_USER_KEY, callingUser);
58506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            if (reqUser != callingUser) {
586d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                callingUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
587d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                        Binder.getCallingUid(), reqUser, false, true,
588d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                        "get/set setting for user", null);
589d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                if (LOCAL_LOGV) Slog.v(TAG, "   access setting for user " + callingUser);
59006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            }
59106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
59206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
59361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // Note: we assume that get/put operations for moved-to-global names have already
59461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // been directed to the new location on the caller side (otherwise we'd fix them
59561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // up here).
59661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        DatabaseHelper dbHelper;
59761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        SettingsCache cache;
59861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate
59961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // Get methods
60061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
60161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
60261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            dbHelper = getOrEstablishDatabase(callingUser);
60361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            cache = sSystemCaches.get(callingUser);
60461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
60561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        }
60661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
60761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
60861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            dbHelper = getOrEstablishDatabase(callingUser);
60961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            cache = sSecureCaches.get(callingUser);
61061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            return lookupValue(dbHelper, TABLE_SECURE, cache, request);
61161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        }
61261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
61361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser);
61461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            // fast path: owner db & cache are immutable after onCreate() so we need not
61561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            // guard on the attempt to look them up
61661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            return lookupValue(getOrEstablishDatabase(UserHandle.USER_OWNER), TABLE_GLOBAL,
61761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate                    sGlobalCache, request);
61861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        }
61906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
62061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // Put methods - new value is in the args bundle under the key named by
62161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // the Settings.NameValueTable.VALUE static.
62261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        final String newValue = (args == null)
623961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn                ? null : args.getString(Settings.NameValueTable.VALUE);
624961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn
625961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        // Framework can't do automatic permission checking for calls, so we need
626961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        // to do it here.
627961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS)
628961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn                != PackageManager.PERMISSION_GRANTED) {
629961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn            throw new SecurityException(
630961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn                    String.format("Permission denial: writing to settings requires %1$s",
631961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn                                  android.Manifest.permission.WRITE_SETTINGS));
632961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        }
633961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn
634961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        // Also need to take care of app op.
635961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SETTINGS, Binder.getCallingUid(),
636911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey                getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
637961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn            return null;
638961321fe4ed4431a6362d729d9e4ea26bdecde61Dianne Hackborn        }
63961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate
64061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        final ContentValues values = new ContentValues();
64161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        values.put(Settings.NameValueTable.NAME, request);
64261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        values.put(Settings.NameValueTable.VALUE, newValue);
64361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
64461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
64561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            insertForUser(Settings.System.CONTENT_URI, values, callingUser);
64661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        } else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
64761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
64861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
64961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        } else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
65061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
65161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
65261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        } else {
65361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            Slog.w(TAG, "call() with invalid method: " + method);
65406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
65506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
6561877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        return null;
6571877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    }
6581877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick
6591877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    // Looks up value 'key' in 'table' and returns either a single-pair Bundle,
6601877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    // possibly with a null value, or null on failure.
66106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private Bundle lookupValue(DatabaseHelper dbHelper, String table,
66206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            final SettingsCache cache, String key) {
66306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (cache == null) {
66406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate           Slog.e(TAG, "cache is null for user " + UserHandle.getCallingUserId() + " : key=" + key);
66506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate           return null;
66606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
6671bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        synchronized (cache) {
6680c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson            Bundle value = cache.get(key);
6690c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson            if (value != null) {
670f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                if (value != TOO_LARGE_TO_CACHE_MARKER) {
671f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    return value;
672f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                }
673f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // else we fall through and read the value from disk
674f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            } else if (cache.fullyMatchesDisk()) {
675f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // Fast path (very common).  Don't even try touch disk
676f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // if we know we've slurped it all in.  Trying to
677f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // touch the disk would mean waiting for yaffs2 to
678f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // give us access, which could takes hundreds of
679f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // milliseconds.  And we're very likely being called
680f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // from somebody's UI thread...
681f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return NULL_SETTING;
6821bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
6831bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
6841bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
68506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbHelper.getReadableDatabase();
6861877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        Cursor cursor = null;
6871877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        try {
6881877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
6891877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick                              null, null, null, null);
6901877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            if (cursor != null && cursor.getCount() == 1) {
6911877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick                cursor.moveToFirst();
692342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                return cache.putIfAbsent(key, cursor.getString(0));
6931877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            }
6941877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        } catch (SQLiteException e) {
6951877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            Log.w(TAG, "settings lookup error", e);
6961877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            return null;
6971877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        } finally {
6981877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            if (cursor != null) cursor.close();
6991877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        }
700342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick        cache.putIfAbsent(key, null);
7011bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        return NULL_SETTING;
7021877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    }
7031877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick
70454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
70554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
7064dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        return queryForUser(url, select, where, whereArgs, sort, UserHandle.getCallingUserId());
7074dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate    }
7084dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate
709b7564454297ba1706670ccab0562cac6676d0a77Christopher Tate    private Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs,
7104dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            String sort, int forUser) {
7114dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "query(" + url + ") for user " + forUser);
71254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, where, whereArgs);
71306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        DatabaseHelper dbH;
71478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        dbH = getOrEstablishDatabase(
71578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                TABLE_GLOBAL.equals(args.table) ? UserHandle.USER_OWNER : forUser);
71606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getReadableDatabase();
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
718f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // The favorites table was moved from this provider to a provider inside Home
719f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // Home still need to query this table to upgrade from pre-cupcake builds
720f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // However, a cupcake+ build with no data does not contain this table which will
721f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // cause an exception in the SQL stack. The following line is a special case to
722f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // let the caller of the query have a chance to recover and avoid the exception
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (TABLE_OLD_FAVORITES.equals(args.table)) {
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            args.table = TABLE_FAVORITES;
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Cursor cursor = db.rawQuery("PRAGMA table_info(favorites);", null);
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursor != null) {
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean exists = cursor.getCount() > 0;
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                cursor.close();
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!exists) return null;
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
736f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project
73754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
73854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        qb.setTables(args.table);
73954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
74054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        Cursor ret = qb.query(db, select, args.where, args.args, null, null, sort);
741afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        // the default Cursor interface does not support per-user observation
742afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        try {
743afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            AbstractCursor c = (AbstractCursor) ret;
744afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            c.setNotificationUri(getContext().getContentResolver(), url, forUser);
745afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        } catch (ClassCastException e) {
746afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            // details of the concrete Cursor implementation have changed and this code has
747afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            // not been updated to match -- complain and fail hard.
748afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            Log.wtf(TAG, "Incompatible cursor derivation!");
749afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            throw e;
750afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        }
75154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return ret;
75254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
75354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
75454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
75554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String getType(Uri url) {
75654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // If SqlArguments supplies a where clause, then it must be an item
75754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // (because we aren't supplying our own where clause).
75854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, null, null);
75954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (TextUtils.isEmpty(args.where)) {
76054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return "vnd.android.cursor.dir/" + args.table;
76154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
76254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return "vnd.android.cursor.item/" + args.table;
76354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
76454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
76554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
76654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
76754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public int bulkInsert(Uri uri, ContentValues[] values) {
76806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final int callingUser = UserHandle.getCallingUserId();
76906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "bulkInsert() for user " + callingUser);
77054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(uri);
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
77454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        checkWritePermissions(args);
77506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SettingsCache cache = cacheForTable(callingUser, args.table);
77654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
77706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
77806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
77978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(
78078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                TABLE_GLOBAL.equals(args.table) ? UserHandle.USER_OWNER : callingUser);
78106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
78254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        db.beginTransaction();
78354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        try {
78454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            int numValues = values.length;
78554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            for (int i = 0; i < numValues; i++) {
78654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (db.insert(args.table, null, values[i]) < 0) return 0;
7871bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                SettingsCache.populate(cache, values[i]);
78854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + values[i]);
78954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
79054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            db.setTransactionSuccessful();
79154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } finally {
79254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            db.endTransaction();
79306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mutationCount.decrementAndGet();
79454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
79554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
79606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sendNotify(uri, callingUser);
79754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return values.length;
79854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
79954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
800bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood    /*
801bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * Used to parse changes to the value of Settings.Secure.LOCATION_PROVIDERS_ALLOWED.
802bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * This setting contains a list of the currently enabled location providers.
803bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * But helper functions in android.providers.Settings can enable or disable
804bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * a single provider by using a "+" or "-" prefix before the provider name.
8051bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     *
8061bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * @returns whether the database needs to be updated or not, also modifying
8071bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     *     'initialValues' if needed.
808bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     */
809d2726582f135383e56661bc41d750966642dab45Maggie Benthall    private boolean parseProviderList(Uri url, ContentValues initialValues, int desiredUser) {
810bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        String value = initialValues.getAsString(Settings.Secure.VALUE);
811bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        String newProviders = null;
812bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        if (value != null && value.length() > 1) {
813bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            char prefix = value.charAt(0);
814bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            if (prefix == '+' || prefix == '-') {
815bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                // skip prefix
816bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                value = value.substring(1);
817bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
818bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                // read list of enabled providers into "providers"
819bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                String providers = "";
820bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                String[] columns = {Settings.Secure.VALUE};
821bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                String where = Settings.Secure.NAME + "=\'" + Settings.Secure.LOCATION_PROVIDERS_ALLOWED + "\'";
822d2726582f135383e56661bc41d750966642dab45Maggie Benthall                Cursor cursor = queryForUser(url, columns, where, null, null, desiredUser);
823bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (cursor != null && cursor.getCount() == 1) {
824bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    try {
825bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        cursor.moveToFirst();
826bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        providers = cursor.getString(0);
827bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    } finally {
828bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        cursor.close();
829bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    }
830bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                }
831bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
832bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                int index = providers.indexOf(value);
833bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                int end = index + value.length();
834bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                // check for commas to avoid matching on partial string
835bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (index > 0 && providers.charAt(index - 1) != ',') index = -1;
836bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (end < providers.length() && providers.charAt(end) != ',') index = -1;
837bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
838bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (prefix == '+' && index < 0) {
839bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    // append the provider to the list if not present
840bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    if (providers.length() == 0) {
841bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        newProviders = value;
842bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    } else {
843bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        newProviders = providers + ',' + value;
844bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    }
845bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                } else if (prefix == '-' && index >= 0) {
846bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    // remove the provider from the list if present
847bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    // remove leading or trailing comma
848bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    if (index > 0) {
849bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                        index--;
850bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    } else if (end < providers.length()) {
851bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                        end++;
852bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    }
853bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
854bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    newProviders = providers.substring(0, index);
855bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    if (end < providers.length()) {
856bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        newProviders += providers.substring(end);
857bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    }
858bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                } else {
859bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    // nothing changed, so no need to update the database
860bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    return false;
861bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                }
862bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
863bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (newProviders != null) {
864bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    initialValues.put(Settings.Secure.VALUE, newProviders);
865bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                }
866bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            }
867bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        }
8681bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
869bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        return true;
870bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood    }
871bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
87254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
87354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public Uri insert(Uri url, ContentValues initialValues) {
87406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        return insertForUser(url, initialValues, UserHandle.getCallingUserId());
87506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
87606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
87706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Settings.put*ForUser() always winds up here, so this is where we apply
87806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // policy around permission to write settings for other users.
87906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private Uri insertForUser(Uri url, ContentValues initialValues, int desiredUserHandle) {
88006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final int callingUser = UserHandle.getCallingUserId();
88106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (callingUser != desiredUserHandle) {
8824dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            getContext().enforceCallingOrSelfPermission(
88306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
88406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    "Not permitted to access settings for other users");
88506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
88606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
88706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "insert(" + url + ") for user " + desiredUserHandle
88806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                + " by " + callingUser);
88906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
89054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url);
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
89454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
895bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        // Special case LOCATION_PROVIDERS_ALLOWED.
896bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        // Support enabling/disabling a single provider (using "+" or "-" prefix)
897bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        String name = initialValues.getAsString(Settings.Secure.NAME);
898bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
899d2726582f135383e56661bc41d750966642dab45Maggie Benthall            if (!parseProviderList(url, initialValues, desiredUserHandle)) return null;
900bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        }
901bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
902c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        // If this is an insert() of a key that has been migrated to the global store,
903c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        // redirect the operation to that store
904c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        if (name != null) {
905c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate            if (sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name)) {
906c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                if (!TABLE_GLOBAL.equals(args.table)) {
907c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    if (LOCAL_LOGV) Slog.i(TAG, "Rewrite of insert() of now-global key " + name);
908c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                }
909c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                args.table = TABLE_GLOBAL;  // next condition will rewrite the user handle
910c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate            }
911c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        }
912c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate
91334637e57fc5bce01029806a67cf0cc2ef049e13bChristopher Tate        // Check write permissions only after determining which table the insert will touch
91434637e57fc5bce01029806a67cf0cc2ef049e13bChristopher Tate        checkWritePermissions(args);
91534637e57fc5bce01029806a67cf0cc2ef049e13bChristopher Tate
91606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // The global table is stored under the owner, always
91706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_GLOBAL.equals(args.table)) {
91806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            desiredUserHandle = UserHandle.USER_OWNER;
91906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
92006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
92106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SettingsCache cache = cacheForTable(desiredUserHandle, args.table);
922547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        String value = initialValues.getAsString(Settings.NameValueTable.VALUE);
923547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        if (SettingsCache.isRedundantSetValue(cache, name, value)) {
924547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            return Uri.withAppendedPath(url, name);
925547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        }
926547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick
92706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(desiredUserHandle);
92806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
92978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(desiredUserHandle);
93006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
93154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        final long rowId = db.insert(args.table, null, initialValues);
93206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.decrementAndGet();
93354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (rowId <= 0) return null;
93454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
9351bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        SettingsCache.populate(cache, initialValues);  // before we notify
9361bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
93778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + initialValues
93878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                + " for user " + desiredUserHandle);
93906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Note that we use the original url here, not the potentially-rewritten table name
94054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        url = getUriFor(url, initialValues, rowId);
94106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sendNotify(url, desiredUserHandle);
94254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return url;
94354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
94454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
94554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
94654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public int delete(Uri url, String where, String[] whereArgs) {
9474dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        int callingUser = UserHandle.getCallingUserId();
94806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "delete() for user " + callingUser);
94954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, where, whereArgs);
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (TABLE_OLD_FAVORITES.equals(args.table)) {
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            args.table = TABLE_FAVORITES;
9544dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        } else if (TABLE_GLOBAL.equals(args.table)) {
9554dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            callingUser = UserHandle.USER_OWNER;
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
95754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        checkWritePermissions(args);
95854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
95906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
96006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
96178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(callingUser);
96206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
96354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int count = db.delete(args.table, args.where, args.args);
96406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.decrementAndGet();
9651bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        if (count > 0) {
96606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            invalidateCache(callingUser, args.table);  // before we notify
96706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sendNotify(url, callingUser);
9681bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
96906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        startAsyncCachePopulation(callingUser);
97054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
97154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return count;
97254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
97354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
97454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
97554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) {
97606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // NOTE: update() is never called by the front-end Settings API, and updates that
97706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // wind up affecting rows in Secure that are globally shared will not have the
97806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // intended effect (the update will be invisible to the rest of the system).
97906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // This should have no practical effect, since writes to the Secure db can only
98006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // be done by system code, and that code should be using the correct API up front.
9814dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        int callingUser = UserHandle.getCallingUserId();
98206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "update() for user " + callingUser);
98354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, where, whereArgs);
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9864dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        } else if (TABLE_GLOBAL.equals(args.table)) {
9874dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            callingUser = UserHandle.USER_OWNER;
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
98954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        checkWritePermissions(args);
99054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
99106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
99206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
99378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(callingUser);
99406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
99554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int count = db.update(args.table, initialValues, args.where, args.args);
99606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.decrementAndGet();
9971bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        if (count > 0) {
99806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            invalidateCache(callingUser, args.table);  // before we notify
99906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sendNotify(url, callingUser);
10001bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
100106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        startAsyncCachePopulation(callingUser);
100254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
100354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return count;
100454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
100554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
100654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
100754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
100854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
100954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        /*
101054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project         * When a client attempts to openFile the default ringtone or
101154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project         * notification setting Uri, we will proxy the call to the current
1012853ad6fbe34fa26e81e4b7325309a034d7a1b038Mike Lockwood         * default ringtone's Uri (if it is in the media provider).
1013f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick         */
101454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int ringtoneType = RingtoneManager.getDefaultType(uri);
101554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Above call returns -1 if the Uri doesn't match a default type
101654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (ringtoneType != -1) {
101754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Context context = getContext();
1018f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
101954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            // Get the current value for the default sound
102054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
102154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
102269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            if (soundUri != null) {
1023853ad6fbe34fa26e81e4b7325309a034d7a1b038Mike Lockwood                // Proxy the openFile call to media provider
102454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                String authority = soundUri.getAuthority();
1025853ad6fbe34fa26e81e4b7325309a034d7a1b038Mike Lockwood                if (authority.equals(MediaStore.AUTHORITY)) {
102654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    return context.getContentResolver().openFileDescriptor(soundUri, mode);
102754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
102854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
102954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
103054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
103154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return super.openFile(uri, mode);
103254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
103369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
103469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen    @Override
103569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
103669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
103769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        /*
103869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         * When a client attempts to openFile the default ringtone or
103969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         * notification setting Uri, we will proxy the call to the current
1040853ad6fbe34fa26e81e4b7325309a034d7a1b038Mike Lockwood         * default ringtone's Uri (if it is in the media provider).
104169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         */
104269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        int ringtoneType = RingtoneManager.getDefaultType(uri);
104369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        // Above call returns -1 if the Uri doesn't match a default type
104469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        if (ringtoneType != -1) {
104569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            Context context = getContext();
104669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
104769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            // Get the current value for the default sound
104869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
104969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
105069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            if (soundUri != null) {
1051853ad6fbe34fa26e81e4b7325309a034d7a1b038Mike Lockwood                // Proxy the openFile call to media provider
105269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                String authority = soundUri.getAuthority();
1053853ad6fbe34fa26e81e4b7325309a034d7a1b038Mike Lockwood                if (authority.equals(MediaStore.AUTHORITY)) {
105469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    ParcelFileDescriptor pfd = null;
105569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    try {
105669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        pfd = context.getContentResolver().openFileDescriptor(soundUri, mode);
105769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        return new AssetFileDescriptor(pfd, 0, -1);
105869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    } catch (FileNotFoundException ex) {
105969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        // fall through and open the fallback ringtone below
106069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    }
106169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                }
106269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
106369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                try {
106469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    return super.openAssetFile(soundUri, mode);
106569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                } catch (FileNotFoundException ex) {
106669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    // Since a non-null Uri was specified, but couldn't be opened,
106769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    // fall back to the built-in ringtone.
106869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    return context.getResources().openRawResourceFd(
106969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            com.android.internal.R.raw.fallbackring);
107069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                }
107169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            }
107269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            // no need to fall through and have openFile() try again, since we
107369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            // already know that will fail.
107469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            throw new FileNotFoundException(); // or return null ?
107569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        }
107669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
107769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        // Note that this will end up calling openFile() above.
107869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        return super.openAssetFile(uri, mode);
107969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen    }
10801bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
10811bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    /**
10821bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * In-memory LRU Cache of system and secure settings, along with
10831bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * associated helper functions to keep cache coherent with the
10841bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * database.
10851bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     */
10860c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson    private static final class SettingsCache extends LruCache<String, Bundle> {
1087342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick
1088f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private final String mCacheName;
1089f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private boolean mCacheFullyMatchesDisk = false;  // has the whole database slurped.
1090f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1091f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public SettingsCache(String name) {
10920c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson            super(MAX_CACHE_ENTRIES);
1093f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            mCacheName = name;
1094f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
1095f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1096f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        /**
1097f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick         * Is the whole database table slurped into this cache?
1098f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick         */
1099f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public boolean fullyMatchesDisk() {
1100f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (this) {
1101f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return mCacheFullyMatchesDisk;
1102f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
1103f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
1104f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1105f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public void setFullyMatchesDisk(boolean value) {
1106f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (this) {
1107f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                mCacheFullyMatchesDisk = value;
1108f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
11091bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
11101bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
11111bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        @Override
111232c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson        protected void entryRemoved(boolean evicted, String key, Bundle oldValue, Bundle newValue) {
111332c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson            if (evicted) {
111432c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson                mCacheFullyMatchesDisk = false;
111532c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson            }
11161bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
11171bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
1118342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick        /**
1119342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         * Atomic cache population, conditional on size of value and if
1120342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         * we lost a race.
1121342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         *
1122342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         * @returns a Bundle to send back to the client from call(), even
1123342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         *     if we lost the race.
1124342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         */
1125342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick        public Bundle putIfAbsent(String key, String value) {
1126342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick            Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value);
1127342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick            if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
1128342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                synchronized (this) {
11290c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson                    if (get(key) == null) {
1130342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                        put(key, bundle);
1131342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                    }
11321bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                }
11331bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
1134342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick            return bundle;
11351bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
11361bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
11371bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        /**
11381bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick         * Populates a key in a given (possibly-null) cache.
11391bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick         */
11401bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        public static void populate(SettingsCache cache, ContentValues contentValues) {
11411bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            if (cache == null) {
11421bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                return;
11431bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
11441bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            String name = contentValues.getAsString(Settings.NameValueTable.NAME);
11451bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            if (name == null) {
11461bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                Log.w(TAG, "null name populating settings cache.");
11471bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                return;
11481bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
11491bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
1150f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            cache.populate(name, value);
1151f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
1152f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1153f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public void populate(String name, String value) {
1154f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (this) {
1155342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
1156f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
1157342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                } else {
1158f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    put(name, TOO_LARGE_TO_CACHE_MARKER);
1159342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                }
11601bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
11611bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
11621bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
11631bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        /**
1164547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         * For suppressing duplicate/redundant settings inserts early,
1165547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         * checking our cache first (but without faulting it in),
1166547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         * before going to sqlite with the mutation.
1167547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         */
1168547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        public static boolean isRedundantSetValue(SettingsCache cache, String name, String value) {
1169547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            if (cache == null) return false;
1170547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            synchronized (cache) {
1171547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                Bundle bundle = cache.get(name);
1172547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                if (bundle == null) return false;
1173547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                String oldValue = bundle.getPairValue();
1174547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                if (oldValue == null && value == null) return true;
1175547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                if ((oldValue == null) != (value == null)) return false;
1176547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                return oldValue.equals(value);
1177547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            }
1178547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        }
11791bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    }
118054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project}
1181