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;
264528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupManager;
2706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.content.BroadcastReceiver;
2854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.ContentProvider;
2954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.ContentUris;
3054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.ContentValues;
3154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.Context;
3206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.content.Intent;
3306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.content.IntentFilter;
3454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.content.pm.PackageManager;
3569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissenimport android.content.res.AssetFileDescriptor;
36afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tateimport android.database.AbstractCursor;
3754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.Cursor;
3854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
391877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrickimport android.database.sqlite.SQLiteException;
4054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
4154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.media.RingtoneManager;
4254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.net.Uri;
4306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.os.Binder;
441877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrickimport android.os.Bundle;
45f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrickimport android.os.FileObserver;
4654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.os.ParcelFileDescriptor;
4754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.os.SystemProperties;
4806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.os.UserHandle;
4906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.os.UserManager;
5054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.provider.DrmStore;
5154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.provider.MediaStore;
5254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.provider.Settings;
5354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.text.TextUtils;
5454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.util.Log;
550c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilsonimport android.util.LruCache;
5606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.util.Slog;
5706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tateimport android.util.SparseArray;
5854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
5954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectpublic class SettingsProvider extends ContentProvider {
6054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private static final String TAG = "SettingsProvider";
614dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate    private static final boolean LOCAL_LOGV = false;
6254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
6306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final String TABLE_SYSTEM = "system";
6406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final String TABLE_SECURE = "secure";
6506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final String TABLE_GLOBAL = "global";
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TABLE_FAVORITES = "favorites";
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TABLE_OLD_FAVORITES = "old_favorites";
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
691877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    private static final String[] COLUMN_VALUE = new String[] { "value" };
701877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick
7106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Caches for each user's settings, access-ordered for acting as LRU.
721bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    // Guarded by themselves.
73f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    private static final int MAX_CACHE_ENTRIES = 200;
7406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SparseArray<SettingsCache> sSystemCaches
7506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<SettingsCache>();
7606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SparseArray<SettingsCache> sSecureCaches
7706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<SettingsCache>();
7806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SettingsCache sGlobalCache = new SettingsCache(TABLE_GLOBAL);
79f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
80f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // The count of how many known (handled by SettingsProvider)
8106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // database mutations are currently being handled for this user.
8206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Used by file observers to not reload the database when it's ourselves
83f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // modifying it.
8406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static final SparseArray<AtomicInteger> sKnownMutationsInFlight
8506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<AtomicInteger>();
861bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
8778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    // Each defined user has their own settings
8878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
8978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
90342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    // Over this size we don't reject loading or saving settings but
91342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    // we do consider them broken/malicious and don't keep them in
92342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    // memory at least:
93342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick    private static final int MAX_CACHE_ENTRY_SIZE = 500;
94342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick
951bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    private static final Bundle NULL_SETTING = Bundle.forPair("value", null);
961bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
97f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // Used as a sentinel value in an instance equality test when we
98f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // want to cache the existence of a key, but not store its value.
99f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
100f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
10106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private UserManager mUserManager;
1028823c0a8c68fe669c21c539eef9fc6541f0c7494Amith Yamasani    private BackupManager mBackupManager;
10354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
10454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
10506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     * Settings which need to be treated as global/shared in multi-user environments.
10606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     */
10706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    static final HashSet<String> sSecureGlobalKeys;
10806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    static final HashSet<String> sSystemGlobalKeys;
10906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    static {
11006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Keys (name column) from the 'secure' table that are now in the owner user's 'global'
11106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // table, shared across all users
11206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // These must match Settings.Secure.MOVED_TO_GLOBAL
11306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sSecureGlobalKeys = new HashSet<String>();
11466488d64df8c3cf8722b8bf282398617cf3c0551Christopher Tate        Settings.Secure.getMovedKeys(sSecureGlobalKeys);
11506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
11606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Keys from the 'system' table now moved to 'global'
11706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // These must match Settings.System.MOVED_TO_GLOBAL
11806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sSystemGlobalKeys = new HashSet<String>();
11966488d64df8c3cf8722b8bf282398617cf3c0551Christopher Tate        Settings.System.getNonLegacyMovedKeys(sSystemGlobalKeys);
12006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
12106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
12206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private boolean settingMovedToGlobal(final String name) {
12306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        return sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name);
12406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
12506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
12606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    /**
12754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Decode a content URL into the table, projection, and arguments
12854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * used to access the corresponding database rows.
12954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
13054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private static class SqlArguments {
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String table;
13254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        public final String where;
13354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        public final String[] args;
13454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
13554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        /** Operate on existing rows. */
13654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments(Uri url, String where, String[] args) {
13754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (url.getPathSegments().size() == 1) {
138c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                // of the form content://settings/secure, arbitrary where clause
13954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.table = url.getPathSegments().get(0);
14024117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                if (!DatabaseHelper.isValidTable(this.table)) {
14124117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                    throw new IllegalArgumentException("Bad root path: " + this.table);
14224117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                }
14354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.where = where;
14454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.args = args;
14554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else if (url.getPathSegments().size() != 2) {
14654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                throw new IllegalArgumentException("Invalid URI: " + url);
14754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else if (!TextUtils.isEmpty(where)) {
14854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                throw new UnsupportedOperationException("WHERE clause not supported: " + url);
14954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
150c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                // of the form content://settings/secure/element_name, no where clause
15154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.table = url.getPathSegments().get(0);
15224117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                if (!DatabaseHelper.isValidTable(this.table)) {
15324117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                    throw new IllegalArgumentException("Bad root path: " + this.table);
15424117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                }
1555bcb55186ebda12d9e4308043898f7aa3ac5c952Doug Zongker                if (TABLE_SYSTEM.equals(this.table) || TABLE_SECURE.equals(this.table) ||
1565bcb55186ebda12d9e4308043898f7aa3ac5c952Doug Zongker                    TABLE_GLOBAL.equals(this.table)) {
15754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    this.where = Settings.NameValueTable.NAME + "=?";
158c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    final String name = url.getPathSegments().get(1);
159c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    this.args = new String[] { name };
160c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    // Rewrite the table for known-migrated names
161c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    if (TABLE_SYSTEM.equals(this.table) || TABLE_SECURE.equals(this.table)) {
162c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                        if (sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name)) {
163c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                            this.table = TABLE_GLOBAL;
164c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                        }
165c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    }
16654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                } else {
167c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    // of the form content://bookmarks/19
16854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    this.where = "_id=" + ContentUris.parseId(url);
16954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    this.args = null;
17054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
17154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
17254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
17354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
17454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        /** Insert new rows (no where clause allowed). */
17554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments(Uri url) {
17654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (url.getPathSegments().size() == 1) {
17754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.table = url.getPathSegments().get(0);
17824117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                if (!DatabaseHelper.isValidTable(this.table)) {
17924117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                    throw new IllegalArgumentException("Bad root path: " + this.table);
18024117ce3ae32c40798d2d9bda80675814f76730dDianne Hackborn                }
18154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.where = null;
18254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                this.args = null;
18354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
18454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                throw new IllegalArgumentException("Invalid URI: " + url);
18554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
18654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
18754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
18854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
18954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
19054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Get the content URI of a row added to a table.
19154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param tableUri of the entire table
19254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param values found in the row
19354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param rowId of the row
19454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the content URI for this particular row
19554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
19654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private Uri getUriFor(Uri tableUri, ContentValues values, long rowId) {
19754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (tableUri.getPathSegments().size() != 1) {
19854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            throw new IllegalArgumentException("Invalid URI: " + tableUri);
19954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
20054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String table = tableUri.getPathSegments().get(0);
20106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_SYSTEM.equals(table) ||
20206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                TABLE_SECURE.equals(table) ||
20306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                TABLE_GLOBAL.equals(table)) {
20454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String name = values.getAsString(Settings.NameValueTable.NAME);
20554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return Uri.withAppendedPath(tableUri, name);
20654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
20754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return ContentUris.withAppendedId(tableUri, rowId);
20854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
20954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
21054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
21154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
21254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Send a notification when a particular content URI changes.
21354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Modify the system property used to communicate the version of
21454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * this table, for tables which have such a property.  (The Settings
21554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * contract class uses these to provide client-side caches.)
21654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param uri to send notifications for
21754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
21806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void sendNotify(Uri uri, int userHandle) {
21954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Update the system property *first*, so if someone is listening for
22054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // a notification and then using the contract class to get their data,
22154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // the system property will be updated and they'll get the new data.
22254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
223d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani        boolean backedUpDataChanged = false;
22454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String property = null, table = uri.getPathSegments().get(0);
22516aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate        final boolean isGlobal = table.equals(TABLE_GLOBAL);
22606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (table.equals(TABLE_SYSTEM)) {
227139748fd724b482e2c012a6ec44d1c5abc0c0e97Dianne Hackborn            property = Settings.System.SYS_PROP_SETTING_VERSION;
22806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            backedUpDataChanged = true;
22906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        } else if (table.equals(TABLE_SECURE)) {
230139748fd724b482e2c012a6ec44d1c5abc0c0e97Dianne Hackborn            property = Settings.Secure.SYS_PROP_SETTING_VERSION;
231d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani            backedUpDataChanged = true;
23216aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate        } else if (isGlobal) {
23306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            property = Settings.Global.SYS_PROP_SETTING_VERSION;    // this one is global
234d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani            backedUpDataChanged = true;
23554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
23654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
23754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (property != null) {
23854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            long version = SystemProperties.getLong(property, 0) + 1;
23954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (LOCAL_LOGV) Log.v(TAG, "property: " + property + "=" + version);
24054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            SystemProperties.set(property, Long.toString(version));
24154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
24254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
243501eec92f9f4f206ad7972c63f2d0ef0285d8e34-b master        // Inform the backup manager about a data change
244d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani        if (backedUpDataChanged) {
245d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani            mBackupManager.dataChanged();
246d158214511a3c04753de04fa6389e46d33135c38Amith Yamasani        }
24754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Now send the notification through the content framework.
24854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
24954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String notify = uri.getQueryParameter("notify");
25054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (notify == null || "true".equals(notify)) {
25116aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate            final int notifyTarget = isGlobal ? UserHandle.USER_ALL : userHandle;
252c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            final long oldId = Binder.clearCallingIdentity();
253c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            try {
254c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate                getContext().getContentResolver().notifyChange(uri, null, true, notifyTarget);
255c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            } finally {
256c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate                Binder.restoreCallingIdentity(oldId);
257c8459dc85e53a9275c89190b35f1da35cd996e46Christopher Tate            }
25816aa9736175f5bbe924a6e5587a2ca47c2dd702bChristopher Tate            if (LOCAL_LOGV) Log.v(TAG, "notifying for " + notifyTarget + ": " + uri);
25954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
26054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (LOCAL_LOGV) Log.v(TAG, "notification suppressed: " + uri);
26154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
26254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
26354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
26454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
26554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Make sure the caller has permission to write this data.
26654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param args supplied by the caller
26754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @throws SecurityException if the caller is forbidden to write.
26854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
26954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private void checkWritePermissions(SqlArguments args) {
27006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if ((TABLE_SECURE.equals(args.table) || TABLE_GLOBAL.equals(args.table)) &&
271f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project            getContext().checkCallingOrSelfPermission(
272aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker                    android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
273aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker            PackageManager.PERMISSION_GRANTED) {
27416dd82cfdc879b7c3e51b19e54c70dbf78e8d697Brett Chabot            throw new SecurityException(
275aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker                    String.format("Permission denial: writing to secure settings requires %1$s",
276aed8f8eb1491a21c8c71d39258b70edb74533a62Doug Zongker                                  android.Manifest.permission.WRITE_SECURE_SETTINGS));
27754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
27854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
27954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
280f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // FileObserver for external modifications to the database file.
281f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // Note that this is for platform developers only with
282f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // userdebug/eng builds who should be able to tinker with the
283f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // sqlite database out from under the SettingsProvider, which is
284f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // normally the exclusive owner of the database.  But we keep this
285f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // enabled all the time to minimize development-vs-user
286f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // differences in testing.
28706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private static SparseArray<SettingsFileObserver> sObserverInstances
28806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            = new SparseArray<SettingsFileObserver>();
289f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    private class SettingsFileObserver extends FileObserver {
290f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
29106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        private final int mUserHandle;
292f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private final String mPath;
293f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
29406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        public SettingsFileObserver(int userHandle, String path) {
295f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            super(path, FileObserver.CLOSE_WRITE |
296f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                  FileObserver.CREATE | FileObserver.DELETE |
297f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                  FileObserver.MOVED_TO | FileObserver.MODIFY);
29806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mUserHandle = userHandle;
299f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            mPath = path;
300f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
301f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
302f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public void onEvent(int event, String path) {
30306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            int modsInFlight = sKnownMutationsInFlight.get(mUserHandle).get();
304f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            if (modsInFlight > 0) {
305f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // our own modification.
306f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return;
307f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
30806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Log.d(TAG, "User " + mUserHandle + " external modification to " + mPath
30906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    + "; event=" + event);
310f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            if (!mIsDirty.compareAndSet(false, true)) {
311f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // already handled. (we get a few update events
312f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // during an sqlite write)
313f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return;
314f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
31506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Log.d(TAG, "User " + mUserHandle + " updating our caches for " + mPath);
31606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            fullyPopulateCaches(mUserHandle);
317f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            mIsDirty.set(false);
318f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
319f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
320f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
32154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
32254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public boolean onCreate() {
3238823c0a8c68fe669c21c539eef9fc6541f0c7494Amith Yamasani        mBackupManager = new BackupManager(getContext());
32406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
32506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
32678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        establishDbTracking(UserHandle.USER_OWNER);
32778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
32878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        IntentFilter userFilter = new IntentFilter();
32978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        userFilter.addAction(Intent.ACTION_USER_REMOVED);
33078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        getContext().registerReceiver(new BroadcastReceiver() {
33178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            @Override
33278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            public void onReceive(Context context, Intent intent) {
33378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
33478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
33578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                            UserHandle.USER_OWNER);
33678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                    if (userHandle != UserHandle.USER_OWNER) {
33778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                        onUserRemoved(userHandle);
33806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    }
33906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                }
34078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            }
34178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        }, userFilter);
34254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return true;
34354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
34454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
34506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    void onUserRemoved(int userHandle) {
34606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        synchronized (this) {
34778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            // the db file itself will be deleted automatically, but we need to tear down
34878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            // our caches and other internal bookkeeping.
34906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            FileObserver observer = sObserverInstances.get(userHandle);
35006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            if (observer != null) {
35106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                observer.stopWatching();
35206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                sObserverInstances.delete(userHandle);
353f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
35406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
35506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mOpenHelpers.delete(userHandle);
35606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sSystemCaches.delete(userHandle);
35706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sSecureCaches.delete(userHandle);
35806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sKnownMutationsInFlight.delete(userHandle);
35906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
36006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
36106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
36278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    private void establishDbTracking(int userHandle) {
36306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) {
36406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Slog.i(TAG, "Installing settings db helper and caches for user " + userHandle);
36506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
36606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
36778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbhelper;
36806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
36978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        synchronized (this) {
37078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            dbhelper = mOpenHelpers.get(userHandle);
37178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            if (dbhelper == null) {
37278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                dbhelper = new DatabaseHelper(getContext(), userHandle);
37378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                mOpenHelpers.append(userHandle, dbhelper);
37478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
37578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sSystemCaches.append(userHandle, new SettingsCache(TABLE_SYSTEM));
37678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sSecureCaches.append(userHandle, new SettingsCache(TABLE_SECURE));
37778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sKnownMutationsInFlight.append(userHandle, new AtomicInteger(0));
37878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            }
37978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        }
38078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate
38178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // Initialization of the db *outside* the locks.  It's possible that racing
38278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // threads might wind up here, the second having read the cache entries
38378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // written by the first, but that's benign: the SQLite helper implementation
38478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // manages concurrency itself, and it's important that we not run the db
38578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // initialization with any of our own locks held, so we're fine.
38606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbhelper.getWritableDatabase();
38706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
38878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // Watch for external modifications to the database files,
38978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // keeping our caches in sync.  We synchronize the observer set
39078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // separately, and of course it has to run after the db file
39178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        // itself was set up by the DatabaseHelper.
39278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        synchronized (sObserverInstances) {
39378d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            if (sObserverInstances.get(userHandle) == null) {
39478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                SettingsFileObserver observer = new SettingsFileObserver(userHandle, db.getPath());
39578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                sObserverInstances.append(userHandle, observer);
39678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                observer.startWatching();
39778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate            }
39878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        }
39906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
4004dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        ensureAndroidIdIsSet(userHandle);
4014dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate
40206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        startAsyncCachePopulation(userHandle);
40306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
40406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
40506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    class CachePrefetchThread extends Thread {
40606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        private int mUserHandle;
40706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
40806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        CachePrefetchThread(int userHandle) {
40906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            super("populate-settings-caches");
41006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mUserHandle = userHandle;
41106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
41206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
41306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        @Override
41406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        public void run() {
41506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            fullyPopulateCaches(mUserHandle);
41606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
417f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
418f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
41906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void startAsyncCachePopulation(int userHandle) {
42006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        new CachePrefetchThread(userHandle).start();
42106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
42206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
42306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void fullyPopulateCaches(final int userHandle) {
42406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        DatabaseHelper dbHelper = mOpenHelpers.get(userHandle);
42506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Only populate the globals cache once, for the owning user
42606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (userHandle == UserHandle.USER_OWNER) {
42706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            fullyPopulateCache(dbHelper, TABLE_GLOBAL, sGlobalCache);
42806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
42906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        fullyPopulateCache(dbHelper, TABLE_SECURE, sSecureCaches.get(userHandle));
43006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        fullyPopulateCache(dbHelper, TABLE_SYSTEM, sSystemCaches.get(userHandle));
431f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
432f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
433f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    // Slurp all values (if sane in number & size) into cache.
43406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private void fullyPopulateCache(DatabaseHelper dbHelper, String table, SettingsCache cache) {
43506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbHelper.getReadableDatabase();
436f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        Cursor c = db.query(
437f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            table,
438f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
439f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            null, null, null, null, null,
440f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            "" + (MAX_CACHE_ENTRIES + 1) /* limit */);
441f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        try {
442f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (cache) {
4430c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson                cache.evictAll();
444f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                cache.setFullyMatchesDisk(true);  // optimistic
445f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                int rows = 0;
446f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                while (c.moveToNext()) {
447f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    rows++;
448f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    String name = c.getString(0);
449f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    String value = c.getString(1);
450f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    cache.populate(name, value);
451f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                }
452f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                if (rows > MAX_CACHE_ENTRIES) {
453f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    // Somewhat redundant, as removeEldestEntry() will
454f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    // have already done this, but to be explicit:
455f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    cache.setFullyMatchesDisk(false);
456f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    Log.d(TAG, "row count exceeds max cache entries for table " + table);
457f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                }
45840e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                if (LOCAL_LOGV) Log.d(TAG, "cache for settings table '" + table
45940e9f2922cae76ffcbc521481e5be8e80e8744efDianne Hackborn                        + "' rows=" + rows + "; fullycached=" + cache.fullyMatchesDisk());
460f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
461f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        } finally {
462f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            c.close();
463f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
464f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick    }
465f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
4664dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate    private boolean ensureAndroidIdIsSet(int userHandle) {
4674dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        final Cursor c = queryForUser(Settings.Secure.CONTENT_URI,
468c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                new String[] { Settings.NameValueTable.VALUE },
469c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                Settings.NameValueTable.NAME + "=?",
4704dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                new String[] { Settings.Secure.ANDROID_ID }, null,
4714dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                userHandle);
472c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana        try {
473c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            final String value = c.moveToNext() ? c.getString(0) : null;
474c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            if (value == null) {
4759bb4ec484b9b9518bf5b17484dcb50727c58b5d1Nick Kralevich                final SecureRandom random = new SecureRandom();
476c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                final String newAndroidIdValue = Long.toHexString(random.nextLong());
477c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                final ContentValues values = new ContentValues();
478c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
479c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
4804dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                final Uri uri = insertForUser(Settings.Secure.CONTENT_URI, values, userHandle);
481c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                if (uri == null) {
4824dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                    Slog.e(TAG, "Unable to generate new ANDROID_ID for user " + userHandle);
483c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                    return false;
484c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana                }
4854dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                Slog.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue
4864dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate                        + "] for user " + userHandle);
487c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            }
488c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            return true;
489c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana        } finally {
490c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana            c.close();
491c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana        }
492c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana    }
493c70239e84d5024c65728ba74fe74c7394b34ac65Fred Quintana
49406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Lazy-initialize the settings caches for non-primary users
49506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private SettingsCache getOrConstructCache(int callingUser, SparseArray<SettingsCache> which) {
49678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        getOrEstablishDatabase(callingUser); // ignore return value; we don't need it
49778d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        return which.get(callingUser);
49806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
49906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
50006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Lazy initialize the database helper and caches for this user, if necessary
50178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate    private DatabaseHelper getOrEstablishDatabase(int callingUser) {
50206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        long oldId = Binder.clearCallingIdentity();
50306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        try {
50406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            DatabaseHelper dbHelper = mOpenHelpers.get(callingUser);
50506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            if (null == dbHelper) {
50678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                establishDbTracking(callingUser);
50706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                dbHelper = mOpenHelpers.get(callingUser);
50806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            }
50906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return dbHelper;
51006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        } finally {
51106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            Binder.restoreCallingIdentity(oldId);
51206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
51306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
51406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
51506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    public SettingsCache cacheForTable(final int callingUser, String tableName) {
51606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_SYSTEM.equals(tableName)) {
51706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return getOrConstructCache(callingUser, sSystemCaches);
51806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
51906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_SECURE.equals(tableName)) {
52006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return getOrConstructCache(callingUser, sSecureCaches);
52106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
52206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_GLOBAL.equals(tableName)) {
52306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return sGlobalCache;
52406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
52506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        return null;
52606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
52706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
52806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    /**
52906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     * Used for wiping a whole cache on deletes when we're not
53006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     * sure what exactly was deleted or changed.
53106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate     */
53206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    public void invalidateCache(final int callingUser, String tableName) {
53306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SettingsCache cache = cacheForTable(callingUser, tableName);
53406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (cache == null) {
53506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            return;
53606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
53706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        synchronized (cache) {
53806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            cache.evictAll();
53906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            cache.mCacheFullyMatchesDisk = false;
54006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
54106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
54206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
5431877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    /**
5441877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick     * Fast path that avoids the use of chatty remoted Cursors.
5451877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick     */
5461877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    @Override
5471877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    public Bundle call(String method, String request, Bundle args) {
54806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        int callingUser = UserHandle.getCallingUserId();
54906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (args != null) {
55006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            int reqUser = args.getInt(Settings.CALL_METHOD_USER_KEY, callingUser);
55106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            if (reqUser != callingUser) {
552d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                callingUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
553d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                        Binder.getCallingUid(), reqUser, false, true,
554d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                        "get/set setting for user", null);
555d5fe1479248fa597efc7ccb0b36df0b520bbc2a3Christopher Tate                if (LOCAL_LOGV) Slog.v(TAG, "   access setting for user " + callingUser);
55606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            }
55706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
55806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
55961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // Note: we assume that get/put operations for moved-to-global names have already
56061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // been directed to the new location on the caller side (otherwise we'd fix them
56161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // up here).
56261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        DatabaseHelper dbHelper;
56361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        SettingsCache cache;
56461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate
56561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // Get methods
56661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
56761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
56861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            dbHelper = getOrEstablishDatabase(callingUser);
56961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            cache = sSystemCaches.get(callingUser);
57061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
57161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        }
57261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
57361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
57461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            dbHelper = getOrEstablishDatabase(callingUser);
57561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            cache = sSecureCaches.get(callingUser);
57661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            return lookupValue(dbHelper, TABLE_SECURE, cache, request);
57761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        }
57861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
57961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser);
58061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            // fast path: owner db & cache are immutable after onCreate() so we need not
58161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            // guard on the attempt to look them up
58261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            return lookupValue(getOrEstablishDatabase(UserHandle.USER_OWNER), TABLE_GLOBAL,
58361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate                    sGlobalCache, request);
58461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        }
58506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
58661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // Put methods - new value is in the args bundle under the key named by
58761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        // the Settings.NameValueTable.VALUE static.
58861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        final String newValue = (args == null)
58961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        ? null : args.getString(Settings.NameValueTable.VALUE);
59061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate
59161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        final ContentValues values = new ContentValues();
59261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        values.put(Settings.NameValueTable.NAME, request);
59361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        values.put(Settings.NameValueTable.VALUE, newValue);
59461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
59561695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
59661695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            insertForUser(Settings.System.CONTENT_URI, values, callingUser);
59761695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        } else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
59861695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
59961695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
60061695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        } else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
60161695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
60261695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
60361695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate        } else {
60461695ffcbccc6cca210e869eb3bc6e97127c2357Christopher Tate            Slog.w(TAG, "call() with invalid method: " + method);
60506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
60606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
6071877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        return null;
6081877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    }
6091877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick
6101877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    // Looks up value 'key' in 'table' and returns either a single-pair Bundle,
6111877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    // possibly with a null value, or null on failure.
61206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private Bundle lookupValue(DatabaseHelper dbHelper, String table,
61306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            final SettingsCache cache, String key) {
61406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (cache == null) {
61506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate           Slog.e(TAG, "cache is null for user " + UserHandle.getCallingUserId() + " : key=" + key);
61606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate           return null;
61706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
6181bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        synchronized (cache) {
6190c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson            Bundle value = cache.get(key);
6200c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson            if (value != null) {
621f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                if (value != TOO_LARGE_TO_CACHE_MARKER) {
622f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    return value;
623f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                }
624f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // else we fall through and read the value from disk
625f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            } else if (cache.fullyMatchesDisk()) {
626f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // Fast path (very common).  Don't even try touch disk
627f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // if we know we've slurped it all in.  Trying to
628f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // touch the disk would mean waiting for yaffs2 to
629f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // give us access, which could takes hundreds of
630f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // milliseconds.  And we're very likely being called
631f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                // from somebody's UI thread...
632f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return NULL_SETTING;
6331bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
6341bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
6351bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
63606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbHelper.getReadableDatabase();
6371877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        Cursor cursor = null;
6381877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        try {
6391877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
6401877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick                              null, null, null, null);
6411877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            if (cursor != null && cursor.getCount() == 1) {
6421877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick                cursor.moveToFirst();
643342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                return cache.putIfAbsent(key, cursor.getString(0));
6441877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            }
6451877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        } catch (SQLiteException e) {
6461877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            Log.w(TAG, "settings lookup error", e);
6471877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            return null;
6481877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        } finally {
6491877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick            if (cursor != null) cursor.close();
6501877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick        }
651342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick        cache.putIfAbsent(key, null);
6521bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        return NULL_SETTING;
6531877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick    }
6541877d0158b529663b8315482e7346a7bcaa96166Brad Fitzpatrick
65554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
65654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
6574dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        return queryForUser(url, select, where, whereArgs, sort, UserHandle.getCallingUserId());
6584dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate    }
6594dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate
660b7564454297ba1706670ccab0562cac6676d0a77Christopher Tate    private Cursor queryForUser(Uri url, String[] select, String where, String[] whereArgs,
6614dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            String sort, int forUser) {
6624dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "query(" + url + ") for user " + forUser);
66354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, where, whereArgs);
66406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        DatabaseHelper dbH;
66578d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        dbH = getOrEstablishDatabase(
66678d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                TABLE_GLOBAL.equals(args.table) ? UserHandle.USER_OWNER : forUser);
66706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getReadableDatabase();
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // The favorites table was moved from this provider to a provider inside Home
670f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // Home still need to query this table to upgrade from pre-cupcake builds
671f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // However, a cupcake+ build with no data does not contain this table which will
672f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // cause an exception in the SQL stack. The following line is a special case to
673f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project        // let the caller of the query have a chance to recover and avoid the exception
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (TABLE_OLD_FAVORITES.equals(args.table)) {
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            args.table = TABLE_FAVORITES;
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Cursor cursor = db.rawQuery("PRAGMA table_info(favorites);", null);
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursor != null) {
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean exists = cursor.getCount() > 0;
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                cursor.close();
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!exists) return null;
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
687f013e1afd1e68af5e3b868c26a653bbfb39538f8The Android Open Source Project
68854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
68954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        qb.setTables(args.table);
69054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
69154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        Cursor ret = qb.query(db, select, args.where, args.args, null, null, sort);
692afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        // the default Cursor interface does not support per-user observation
693afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        try {
694afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            AbstractCursor c = (AbstractCursor) ret;
695afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            c.setNotificationUri(getContext().getContentResolver(), url, forUser);
696afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        } catch (ClassCastException e) {
697afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            // details of the concrete Cursor implementation have changed and this code has
698afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            // not been updated to match -- complain and fail hard.
699afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            Log.wtf(TAG, "Incompatible cursor derivation!");
700afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate            throw e;
701afccaa84c8d1b9aa45040ddeb0edd42ba80e80d6Christopher Tate        }
70254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return ret;
70354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
70454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
70554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
70654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String getType(Uri url) {
70754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // If SqlArguments supplies a where clause, then it must be an item
70854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // (because we aren't supplying our own where clause).
70954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, null, null);
71054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (TextUtils.isEmpty(args.where)) {
71154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return "vnd.android.cursor.dir/" + args.table;
71254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
71354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return "vnd.android.cursor.item/" + args.table;
71454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
71554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
71654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
71754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
71854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public int bulkInsert(Uri uri, ContentValues[] values) {
71906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final int callingUser = UserHandle.getCallingUserId();
72006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "bulkInsert() for user " + callingUser);
72154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(uri);
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
72554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        checkWritePermissions(args);
72606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SettingsCache cache = cacheForTable(callingUser, args.table);
72754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
72806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
72906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
73078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(
73178d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                TABLE_GLOBAL.equals(args.table) ? UserHandle.USER_OWNER : callingUser);
73206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
73354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        db.beginTransaction();
73454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        try {
73554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            int numValues = values.length;
73654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            for (int i = 0; i < numValues; i++) {
73754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (db.insert(args.table, null, values[i]) < 0) return 0;
7381bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                SettingsCache.populate(cache, values[i]);
73954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + values[i]);
74054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
74154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            db.setTransactionSuccessful();
74254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } finally {
74354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            db.endTransaction();
74406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            mutationCount.decrementAndGet();
74554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
74654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
74706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sendNotify(uri, callingUser);
74854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return values.length;
74954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
75054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
751bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood    /*
752bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * Used to parse changes to the value of Settings.Secure.LOCATION_PROVIDERS_ALLOWED.
753bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * This setting contains a list of the currently enabled location providers.
754bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * But helper functions in android.providers.Settings can enable or disable
755bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     * a single provider by using a "+" or "-" prefix before the provider name.
7561bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     *
7571bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * @returns whether the database needs to be updated or not, also modifying
7581bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     *     'initialValues' if needed.
759bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood     */
760bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood    private boolean parseProviderList(Uri url, ContentValues initialValues) {
761bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        String value = initialValues.getAsString(Settings.Secure.VALUE);
762bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        String newProviders = null;
763bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        if (value != null && value.length() > 1) {
764bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            char prefix = value.charAt(0);
765bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            if (prefix == '+' || prefix == '-') {
766bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                // skip prefix
767bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                value = value.substring(1);
768bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
769bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                // read list of enabled providers into "providers"
770bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                String providers = "";
771bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                String[] columns = {Settings.Secure.VALUE};
772bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                String where = Settings.Secure.NAME + "=\'" + Settings.Secure.LOCATION_PROVIDERS_ALLOWED + "\'";
773bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                Cursor cursor = query(url, columns, where, null, null);
774bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (cursor != null && cursor.getCount() == 1) {
775bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    try {
776bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        cursor.moveToFirst();
777bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        providers = cursor.getString(0);
778bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    } finally {
779bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        cursor.close();
780bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    }
781bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                }
782bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
783bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                int index = providers.indexOf(value);
784bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                int end = index + value.length();
785bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                // check for commas to avoid matching on partial string
786bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (index > 0 && providers.charAt(index - 1) != ',') index = -1;
787bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (end < providers.length() && providers.charAt(end) != ',') index = -1;
788bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
789bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (prefix == '+' && index < 0) {
790bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    // append the provider to the list if not present
791bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    if (providers.length() == 0) {
792bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        newProviders = value;
793bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    } else {
794bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        newProviders = providers + ',' + value;
795bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    }
796bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                } else if (prefix == '-' && index >= 0) {
797bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    // remove the provider from the list if present
798bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    // remove leading or trailing comma
799bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    if (index > 0) {
800bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                        index--;
801bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    } else if (end < providers.length()) {
802bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                        end++;
803bdc7f891cf47c077c26ef418dbea23c04820c152Mike Lockwood                    }
804bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
805bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    newProviders = providers.substring(0, index);
806bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    if (end < providers.length()) {
807bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                        newProviders += providers.substring(end);
808bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    }
809bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                } else {
810bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    // nothing changed, so no need to update the database
811bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    return false;
812bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                }
813bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
814bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                if (newProviders != null) {
815bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                    initialValues.put(Settings.Secure.VALUE, newProviders);
816bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood                }
817bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            }
818bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        }
8191bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
820bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        return true;
821bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood    }
822bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
82354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
82454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public Uri insert(Uri url, ContentValues initialValues) {
82506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        return insertForUser(url, initialValues, UserHandle.getCallingUserId());
82606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    }
82706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
82806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // Settings.put*ForUser() always winds up here, so this is where we apply
82906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    // policy around permission to write settings for other users.
83006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate    private Uri insertForUser(Uri url, ContentValues initialValues, int desiredUserHandle) {
83106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final int callingUser = UserHandle.getCallingUserId();
83206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (callingUser != desiredUserHandle) {
8334dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            getContext().enforceCallingOrSelfPermission(
83406efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
83506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                    "Not permitted to access settings for other users");
83606efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
83706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
83806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "insert(" + url + ") for user " + desiredUserHandle
83906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate                + " by " + callingUser);
84006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
84154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url);
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
84554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
846bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        // Special case LOCATION_PROVIDERS_ALLOWED.
847bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        // Support enabling/disabling a single provider (using "+" or "-" prefix)
848bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        String name = initialValues.getAsString(Settings.Secure.NAME);
849bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
850bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood            if (!parseProviderList(url, initialValues)) return null;
851bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood        }
852bd2a7126e5b42e022228c6aac25e95b671e5263bMike Lockwood
853c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        // If this is an insert() of a key that has been migrated to the global store,
854c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        // redirect the operation to that store
855c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        if (name != null) {
856c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate            if (sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name)) {
857c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                if (!TABLE_GLOBAL.equals(args.table)) {
858c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                    if (LOCAL_LOGV) Slog.i(TAG, "Rewrite of insert() of now-global key " + name);
859c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                }
860c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate                args.table = TABLE_GLOBAL;  // next condition will rewrite the user handle
861c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate            }
862c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate        }
863c221d2be7d2bf57373d43457b18483266f88f9a6Christopher Tate
86434637e57fc5bce01029806a67cf0cc2ef049e13bChristopher Tate        // Check write permissions only after determining which table the insert will touch
86534637e57fc5bce01029806a67cf0cc2ef049e13bChristopher Tate        checkWritePermissions(args);
86634637e57fc5bce01029806a67cf0cc2ef049e13bChristopher Tate
86706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // The global table is stored under the owner, always
86806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (TABLE_GLOBAL.equals(args.table)) {
86906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            desiredUserHandle = UserHandle.USER_OWNER;
87006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        }
87106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate
87206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SettingsCache cache = cacheForTable(desiredUserHandle, args.table);
873547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        String value = initialValues.getAsString(Settings.NameValueTable.VALUE);
874547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        if (SettingsCache.isRedundantSetValue(cache, name, value)) {
875547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            return Uri.withAppendedPath(url, name);
876547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        }
877547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick
87806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(desiredUserHandle);
87906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
88078d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(desiredUserHandle);
88106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
88254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        final long rowId = db.insert(args.table, null, initialValues);
88306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.decrementAndGet();
88454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (rowId <= 0) return null;
88554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
8861bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        SettingsCache.populate(cache, initialValues);  // before we notify
8871bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
88878d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + initialValues
88978d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate                + " for user " + desiredUserHandle);
89006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // Note that we use the original url here, not the potentially-rewritten table name
89154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        url = getUriFor(url, initialValues, rowId);
89206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        sendNotify(url, desiredUserHandle);
89354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return url;
89454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
89554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
89654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
89754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public int delete(Uri url, String where, String[] whereArgs) {
8984dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        int callingUser = UserHandle.getCallingUserId();
89906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "delete() for user " + callingUser);
90054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, where, whereArgs);
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (TABLE_OLD_FAVORITES.equals(args.table)) {
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            args.table = TABLE_FAVORITES;
9054dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        } else if (TABLE_GLOBAL.equals(args.table)) {
9064dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            callingUser = UserHandle.USER_OWNER;
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
90854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        checkWritePermissions(args);
90954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
91006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
91106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
91278d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(callingUser);
91306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
91454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int count = db.delete(args.table, args.where, args.args);
91506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.decrementAndGet();
9161bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        if (count > 0) {
91706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            invalidateCache(callingUser, args.table);  // before we notify
91806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sendNotify(url, callingUser);
9191bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
92006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        startAsyncCachePopulation(callingUser);
92154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
92254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return count;
92354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
92454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
92554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
92654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) {
92706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // NOTE: update() is never called by the front-end Settings API, and updates that
92806efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // wind up affecting rows in Secure that are globally shared will not have the
92906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // intended effect (the update will be invisible to the rest of the system).
93006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // This should have no practical effect, since writes to the Secure db can only
93106efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        // be done by system code, and that code should be using the correct API up front.
9324dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        int callingUser = UserHandle.getCallingUserId();
93306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        if (LOCAL_LOGV) Slog.v(TAG, "update() for user " + callingUser);
93454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        SqlArguments args = new SqlArguments(url, where, whereArgs);
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TABLE_FAVORITES.equals(args.table)) {
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
9374dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate        } else if (TABLE_GLOBAL.equals(args.table)) {
9384dc7a68dbeaa0edd8815b2105915753310d58343Christopher Tate            callingUser = UserHandle.USER_OWNER;
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
94054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        checkWritePermissions(args);
94154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
94206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
94306efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.incrementAndGet();
94478d2a66ac12e4c8f1303225514f573fb53af1dd9Christopher Tate        DatabaseHelper dbH = getOrEstablishDatabase(callingUser);
94506efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        SQLiteDatabase db = dbH.getWritableDatabase();
94654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int count = db.update(args.table, initialValues, args.where, args.args);
94706efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        mutationCount.decrementAndGet();
9481bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        if (count > 0) {
94906efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            invalidateCache(callingUser, args.table);  // before we notify
95006efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate            sendNotify(url, callingUser);
9511bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
95206efb530a479ea12398c1b3ee4b80e2ac85a1680Christopher Tate        startAsyncCachePopulation(callingUser);
95354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
95454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return count;
95554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
95654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
95754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    @Override
95854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
95954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
96054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        /*
96154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project         * When a client attempts to openFile the default ringtone or
96254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project         * notification setting Uri, we will proxy the call to the current
96354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project         * default ringtone's Uri (if it is in the DRM or media provider).
964f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick         */
96554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int ringtoneType = RingtoneManager.getDefaultType(uri);
96654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Above call returns -1 if the Uri doesn't match a default type
96754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (ringtoneType != -1) {
96854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Context context = getContext();
969f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
97054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            // Get the current value for the default sound
97154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
97254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
97369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            if (soundUri != null) {
97454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                // Only proxy the openFile call to drm or media providers
97554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                String authority = soundUri.getAuthority();
97654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
97754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
97854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
97954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    if (isDrmAuthority) {
98054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        try {
98154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                            // Check DRM access permission here, since once we
98254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                            // do the below call the DRM will be checking our
98354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                            // permission, not our caller's permission
98454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                            DrmStore.enforceAccessDrmPermission(context);
98554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        } catch (SecurityException e) {
98654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                            throw new FileNotFoundException(e.getMessage());
98754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        }
98854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    }
989f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
99054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    return context.getContentResolver().openFileDescriptor(soundUri, mode);
99154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
99254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
99354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
99454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
99554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return super.openFile(uri, mode);
99654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
99769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
99869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen    @Override
99969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
100069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
100169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        /*
100269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         * When a client attempts to openFile the default ringtone or
100369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         * notification setting Uri, we will proxy the call to the current
100469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         * default ringtone's Uri (if it is in the DRM or media provider).
100569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen         */
100669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        int ringtoneType = RingtoneManager.getDefaultType(uri);
100769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        // Above call returns -1 if the Uri doesn't match a default type
100869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        if (ringtoneType != -1) {
100969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            Context context = getContext();
101069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
101169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            // Get the current value for the default sound
101269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
101369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
101469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            if (soundUri != null) {
101569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                // Only proxy the openFile call to drm or media providers
101669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                String authority = soundUri.getAuthority();
101769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
101869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
101969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
102069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    if (isDrmAuthority) {
102169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        try {
102269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            // Check DRM access permission here, since once we
102369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            // do the below call the DRM will be checking our
102469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            // permission, not our caller's permission
102569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            DrmStore.enforceAccessDrmPermission(context);
102669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        } catch (SecurityException e) {
102769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            throw new FileNotFoundException(e.getMessage());
102869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        }
102969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    }
103069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
103169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    ParcelFileDescriptor pfd = null;
103269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    try {
103369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        pfd = context.getContentResolver().openFileDescriptor(soundUri, mode);
103469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        return new AssetFileDescriptor(pfd, 0, -1);
103569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    } catch (FileNotFoundException ex) {
103669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                        // fall through and open the fallback ringtone below
103769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    }
103869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                }
103969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
104069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                try {
104169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    return super.openAssetFile(soundUri, mode);
104269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                } catch (FileNotFoundException ex) {
104369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    // Since a non-null Uri was specified, but couldn't be opened,
104469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    // fall back to the built-in ringtone.
104569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                    return context.getResources().openRawResourceFd(
104669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                            com.android.internal.R.raw.fallbackring);
104769f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen                }
104869f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            }
104969f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            // no need to fall through and have openFile() try again, since we
105069f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            // already know that will fail.
105169f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen            throw new FileNotFoundException(); // or return null ?
105269f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        }
105369f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen
105469f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        // Note that this will end up calling openFile() above.
105569f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen        return super.openAssetFile(uri, mode);
105669f593ccb7414ee98991b1da1a4bfbd9951e3570Marco Nelissen    }
10571bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
10581bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    /**
10591bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * In-memory LRU Cache of system and secure settings, along with
10601bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * associated helper functions to keep cache coherent with the
10611bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     * database.
10621bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick     */
10630c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson    private static final class SettingsCache extends LruCache<String, Bundle> {
1064342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick
1065f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private final String mCacheName;
1066f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        private boolean mCacheFullyMatchesDisk = false;  // has the whole database slurped.
1067f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1068f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public SettingsCache(String name) {
10690c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson            super(MAX_CACHE_ENTRIES);
1070f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            mCacheName = name;
1071f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
1072f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1073f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        /**
1074f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick         * Is the whole database table slurped into this cache?
1075f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick         */
1076f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public boolean fullyMatchesDisk() {
1077f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (this) {
1078f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                return mCacheFullyMatchesDisk;
1079f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
1080f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
1081f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1082f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public void setFullyMatchesDisk(boolean value) {
1083f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (this) {
1084f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                mCacheFullyMatchesDisk = value;
1085f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            }
10861bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
10871bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
10881bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        @Override
108932c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson        protected void entryRemoved(boolean evicted, String key, Bundle oldValue, Bundle newValue) {
109032c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson            if (evicted) {
109132c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson                mCacheFullyMatchesDisk = false;
109232c80a27dae4a3094f647bb4d97b27a0eb3b985eJesse Wilson            }
10931bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
10941bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
1095342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick        /**
1096342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         * Atomic cache population, conditional on size of value and if
1097342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         * we lost a race.
1098342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         *
1099342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         * @returns a Bundle to send back to the client from call(), even
1100342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         *     if we lost the race.
1101342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick         */
1102342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick        public Bundle putIfAbsent(String key, String value) {
1103342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick            Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value);
1104342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick            if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
1105342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                synchronized (this) {
11060c7faeee47e7629f2d23a2e3b25bc4f121252080Jesse Wilson                    if (get(key) == null) {
1107342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                        put(key, bundle);
1108342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                    }
11091bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                }
11101bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
1111342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick            return bundle;
11121bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
11131bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
11141bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        /**
11151bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick         * Populates a key in a given (possibly-null) cache.
11161bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick         */
11171bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        public static void populate(SettingsCache cache, ContentValues contentValues) {
11181bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            if (cache == null) {
11191bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                return;
11201bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
11211bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            String name = contentValues.getAsString(Settings.NameValueTable.NAME);
11221bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            if (name == null) {
11231bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                Log.w(TAG, "null name populating settings cache.");
11241bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick                return;
11251bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
11261bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
1127f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            cache.populate(name, value);
1128f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        }
1129f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick
1130f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick        public void populate(String name, String value) {
1131f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick            synchronized (this) {
1132342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {
1133f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value));
1134342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                } else {
1135f366a9b007909cc6d214fbee26a97e880734a094Brad Fitzpatrick                    put(name, TOO_LARGE_TO_CACHE_MARKER);
1136342984a17ddd010381c462066e33e18354b79e4fBrad Fitzpatrick                }
11371bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick            }
11381bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        }
11391bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick
11401bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick        /**
1141547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         * For suppressing duplicate/redundant settings inserts early,
1142547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         * checking our cache first (but without faulting it in),
1143547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         * before going to sqlite with the mutation.
1144547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick         */
1145547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        public static boolean isRedundantSetValue(SettingsCache cache, String name, String value) {
1146547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            if (cache == null) return false;
1147547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            synchronized (cache) {
1148547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                Bundle bundle = cache.get(name);
1149547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                if (bundle == null) return false;
1150547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                String oldValue = bundle.getPairValue();
1151547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                if (oldValue == null && value == null) return true;
1152547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                if ((oldValue == null) != (value == null)) return false;
1153547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick                return oldValue.equals(value);
1154547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick            }
1155547a96bc12f25f585271c678395d4c991f08c52dBrad Fitzpatrick        }
11561bd62bd3ca4d098196e91b43799d4010c1d26623Brad Fitzpatrick    }
115754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project}
1158