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