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