SettingsProvider.java revision b505ccc90667ad69a1b122f025a415a3b2aee6af
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.providers.settings; 18 19import android.Manifest; 20import android.app.ActivityManager; 21import android.app.AppOpsManager; 22import android.app.backup.BackupManager; 23import android.content.BroadcastReceiver; 24import android.content.ContentProvider; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.PackageInfo; 31import android.content.pm.PackageManager; 32import android.content.pm.UserInfo; 33import android.database.Cursor; 34import android.database.MatrixCursor; 35import android.database.sqlite.SQLiteDatabase; 36import android.database.sqlite.SQLiteQueryBuilder; 37import android.hardware.camera2.utils.ArrayUtils; 38import android.net.Uri; 39import android.os.Binder; 40import android.os.Build; 41import android.os.Bundle; 42import android.os.DropBoxManager; 43import android.os.Environment; 44import android.os.ParcelFileDescriptor; 45import android.os.Process; 46import android.os.SystemProperties; 47import android.os.UserHandle; 48import android.os.UserManager; 49import android.provider.Settings; 50import android.text.TextUtils; 51import android.util.ArrayMap; 52import android.util.ArraySet; 53import android.util.Slog; 54import android.util.SparseArray; 55import com.android.internal.annotations.GuardedBy; 56import com.android.internal.content.PackageMonitor; 57import com.android.internal.os.BackgroundThread; 58import java.io.File; 59import java.io.FileDescriptor; 60import java.io.FileNotFoundException; 61import java.io.PrintWriter; 62import java.security.SecureRandom; 63import java.util.Arrays; 64import java.util.List; 65import java.util.Map; 66import java.util.Set; 67import java.util.regex.Pattern; 68 69import com.android.providers.settings.SettingsState.Setting; 70 71/** 72 * <p> 73 * This class is a content provider that publishes the system settings. 74 * It can be accessed via the content provider APIs or via custom call 75 * commands. The latter is a bit faster and is the preferred way to access 76 * the platform settings. 77 * </p> 78 * <p> 79 * There are three settings types, global (with signature level protection 80 * and shared across users), secure (with signature permission level 81 * protection and per user), and system (with dangerous permission level 82 * protection and per user). Global settings are stored under the device owner. 83 * Each of these settings is represented by a {@link 84 * com.android.providers.settings.SettingsState} object mapped to an integer 85 * key derived from the setting type in the most significant bits and user 86 * id in the least significant bits. Settings are synchronously loaded on 87 * instantiation of a SettingsState and asynchronously persisted on mutation. 88 * Settings are stored in the user specific system directory. 89 * </p> 90 * <p> 91 * Apps targeting APIs Lollipop MR1 and lower can add custom settings entries 92 * and get a warning. Targeting higher API version prohibits this as the 93 * system settings are not a place for apps to save their state. When a package 94 * is removed the settings it added are deleted. Apps cannot delete system 95 * settings added by the platform. System settings values are validated to 96 * ensure the clients do not put bad values. Global and secure settings are 97 * changed only by trusted parties, therefore no validation is performed. Also 98 * there is a limit on the amount of app specific settings that can be added 99 * to prevent unlimited growth of the system process memory footprint. 100 * </p> 101 */ 102@SuppressWarnings("deprecation") 103public class SettingsProvider extends ContentProvider { 104 private static final boolean DEBUG = false; 105 106 private static final boolean DROP_DATABASE_ON_MIGRATION = !Build.IS_DEBUGGABLE; 107 108 private static final String LOG_TAG = "SettingsProvider"; 109 110 private static final String TABLE_SYSTEM = "system"; 111 private static final String TABLE_SECURE = "secure"; 112 private static final String TABLE_GLOBAL = "global"; 113 114 // Old tables no longer exist. 115 private static final String TABLE_FAVORITES = "favorites"; 116 private static final String TABLE_OLD_FAVORITES = "old_favorites"; 117 private static final String TABLE_BLUETOOTH_DEVICES = "bluetooth_devices"; 118 private static final String TABLE_BOOKMARKS = "bookmarks"; 119 private static final String TABLE_ANDROID_METADATA = "android_metadata"; 120 121 // The set of removed legacy tables. 122 private static final Set<String> REMOVED_LEGACY_TABLES = new ArraySet<>(); 123 static { 124 REMOVED_LEGACY_TABLES.add(TABLE_FAVORITES); 125 REMOVED_LEGACY_TABLES.add(TABLE_OLD_FAVORITES); 126 REMOVED_LEGACY_TABLES.add(TABLE_BLUETOOTH_DEVICES); 127 REMOVED_LEGACY_TABLES.add(TABLE_BOOKMARKS); 128 REMOVED_LEGACY_TABLES.add(TABLE_ANDROID_METADATA); 129 } 130 131 private static final int MUTATION_OPERATION_INSERT = 1; 132 private static final int MUTATION_OPERATION_DELETE = 2; 133 private static final int MUTATION_OPERATION_UPDATE = 3; 134 135 private static final String[] ALL_COLUMNS = new String[] { 136 Settings.NameValueTable._ID, 137 Settings.NameValueTable.NAME, 138 Settings.NameValueTable.VALUE 139 }; 140 141 private static final Bundle NULL_SETTING = Bundle.forPair(Settings.NameValueTable.VALUE, null); 142 143 // Per user settings that cannot be modified if associated user restrictions are enabled. 144 private static final Map<String, String> sSettingToUserRestrictionMap = new ArrayMap<>(); 145 static { 146 sSettingToUserRestrictionMap.put(Settings.Secure.LOCATION_MODE, 147 UserManager.DISALLOW_SHARE_LOCATION); 148 sSettingToUserRestrictionMap.put(Settings.Secure.LOCATION_PROVIDERS_ALLOWED, 149 UserManager.DISALLOW_SHARE_LOCATION); 150 sSettingToUserRestrictionMap.put(Settings.Secure.INSTALL_NON_MARKET_APPS, 151 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); 152 sSettingToUserRestrictionMap.put(Settings.Global.ADB_ENABLED, 153 UserManager.DISALLOW_DEBUGGING_FEATURES); 154 sSettingToUserRestrictionMap.put(Settings.Global.PACKAGE_VERIFIER_ENABLE, 155 UserManager.ENSURE_VERIFY_APPS); 156 sSettingToUserRestrictionMap.put(Settings.Global.PREFERRED_NETWORK_MODE, 157 UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); 158 } 159 160 // Per user secure settings that moved to the for all users global settings. 161 static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>(); 162 static { 163 Settings.Secure.getMovedToGlobalSettings(sSecureMovedToGlobalSettings); 164 } 165 166 // Per user system settings that moved to the for all users global settings. 167 static final Set<String> sSystemMovedToGlobalSettings = new ArraySet<>(); 168 static { 169 Settings.System.getMovedToGlobalSettings(sSystemMovedToGlobalSettings); 170 } 171 172 // Per user system settings that moved to the per user secure settings. 173 static final Set<String> sSystemMovedToSecureSettings = new ArraySet<>(); 174 static { 175 Settings.System.getMovedToSecureSettings(sSystemMovedToSecureSettings); 176 } 177 178 // Per all users global settings that moved to the per user secure settings. 179 static final Set<String> sGlobalMovedToSecureSettings = new ArraySet<>(); 180 static { 181 Settings.Global.getMovedToSecureSettings(sGlobalMovedToSecureSettings); 182 } 183 184 // Per user secure settings that are cloned for the managed profiles of the user. 185 private static final Set<String> sSecureCloneToManagedSettings = new ArraySet<>(); 186 static { 187 Settings.Secure.getCloneToManagedProfileSettings(sSecureCloneToManagedSettings); 188 } 189 190 // Per user system settings that are cloned for the managed profiles of the user. 191 private static final Set<String> sSystemCloneToManagedSettings = new ArraySet<>(); 192 static { 193 Settings.System.getCloneToManagedProfileSettings(sSystemCloneToManagedSettings); 194 } 195 196 private final Object mLock = new Object(); 197 198 @GuardedBy("mLock") 199 private SettingsRegistry mSettingsRegistry; 200 201 @GuardedBy("mLock") 202 private UserManager mUserManager; 203 204 @GuardedBy("mLock") 205 private AppOpsManager mAppOpsManager; 206 207 @GuardedBy("mLock") 208 private PackageManager mPackageManager; 209 210 @Override 211 public boolean onCreate() { 212 synchronized (mLock) { 213 mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 214 mAppOpsManager = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 215 mPackageManager = getContext().getPackageManager(); 216 mSettingsRegistry = new SettingsRegistry(); 217 } 218 registerBroadcastReceivers(); 219 return true; 220 } 221 222 @Override 223 public Bundle call(String method, String name, Bundle args) { 224 synchronized (mLock) { 225 final int requestingUserId = getRequestingUserId(args); 226 switch (method) { 227 case Settings.CALL_METHOD_GET_GLOBAL: { 228 Setting setting = getGlobalSettingLocked(name); 229 return packageValueForCallResult(setting); 230 } 231 232 case Settings.CALL_METHOD_GET_SECURE: { 233 Setting setting = getSecureSettingLocked(name, requestingUserId); 234 return packageValueForCallResult(setting); 235 } 236 237 case Settings.CALL_METHOD_GET_SYSTEM: { 238 Setting setting = getSystemSettingLocked(name, requestingUserId); 239 return packageValueForCallResult(setting); 240 } 241 242 case Settings.CALL_METHOD_PUT_GLOBAL: { 243 String value = getSettingValue(args); 244 insertGlobalSettingLocked(name, value, requestingUserId); 245 } break; 246 247 case Settings.CALL_METHOD_PUT_SECURE: { 248 String value = getSettingValue(args); 249 insertSecureSettingLocked(name, value, requestingUserId); 250 } break; 251 252 case Settings.CALL_METHOD_PUT_SYSTEM: { 253 String value = getSettingValue(args); 254 insertSystemSettingLocked(name, value, requestingUserId); 255 } break; 256 257 default: { 258 Slog.w(LOG_TAG, "call() with invalid method: " + method); 259 } break; 260 } 261 } 262 return null; 263 } 264 265 @Override 266 public String getType(Uri uri) { 267 Arguments args = new Arguments(uri, null, null, true); 268 if (TextUtils.isEmpty(args.name)) { 269 return "vnd.android.cursor.dir/" + args.table; 270 } else { 271 return "vnd.android.cursor.item/" + args.table; 272 } 273 } 274 275 @Override 276 public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, 277 String order) { 278 if (DEBUG) { 279 Slog.v(LOG_TAG, "query() for user: " + UserHandle.getCallingUserId()); 280 } 281 282 Arguments args = new Arguments(uri, where, whereArgs, true); 283 String[] normalizedProjection = normalizeProjection(projection); 284 285 // If a legacy table that is gone, done. 286 if (REMOVED_LEGACY_TABLES.contains(args.table)) { 287 return new MatrixCursor(normalizedProjection, 0); 288 } 289 290 synchronized (mLock) { 291 switch (args.table) { 292 case TABLE_GLOBAL: { 293 if (args.name != null) { 294 Setting setting = getGlobalSettingLocked(args.name); 295 return packageSettingForQuery(setting, normalizedProjection); 296 } else { 297 return getAllGlobalSettingsLocked(projection); 298 } 299 } 300 301 case TABLE_SECURE: { 302 final int userId = UserHandle.getCallingUserId(); 303 if (args.name != null) { 304 Setting setting = getSecureSettingLocked(args.name, userId); 305 return packageSettingForQuery(setting, normalizedProjection); 306 } else { 307 return getAllSecureSettingsLocked(userId, projection); 308 } 309 } 310 311 case TABLE_SYSTEM: { 312 final int userId = UserHandle.getCallingUserId(); 313 if (args.name != null) { 314 Setting setting = getSystemSettingLocked(args.name, userId); 315 return packageSettingForQuery(setting, normalizedProjection); 316 } else { 317 return getAllSystemSettingsLocked(userId, projection); 318 } 319 } 320 321 default: { 322 throw new IllegalArgumentException("Invalid Uri path:" + uri); 323 } 324 } 325 } 326 } 327 328 @Override 329 public Uri insert(Uri uri, ContentValues values) { 330 if (DEBUG) { 331 Slog.v(LOG_TAG, "insert() for user: " + UserHandle.getCallingUserId()); 332 } 333 334 String table = getValidTableOrThrow(uri); 335 336 // If a legacy table that is gone, done. 337 if (REMOVED_LEGACY_TABLES.contains(table)) { 338 return null; 339 } 340 341 String name = values.getAsString(Settings.Secure.NAME); 342 if (TextUtils.isEmpty(name)) { 343 return null; 344 } 345 346 String value = values.getAsString(Settings.Secure.VALUE); 347 348 synchronized (mLock) { 349 switch (table) { 350 case TABLE_GLOBAL: { 351 if (insertGlobalSettingLocked(name, value, UserHandle.getCallingUserId())) { 352 return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name); 353 } 354 } break; 355 356 case TABLE_SECURE: { 357 if (insertSecureSettingLocked(name, value, UserHandle.getCallingUserId())) { 358 return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name); 359 } 360 } break; 361 362 case TABLE_SYSTEM: { 363 if (insertSystemSettingLocked(name, value, UserHandle.getCallingUserId())) { 364 return Uri.withAppendedPath(Settings.System.CONTENT_URI, name); 365 } 366 } break; 367 368 default: { 369 throw new IllegalArgumentException("Bad Uri path:" + uri); 370 } 371 } 372 } 373 374 return null; 375 } 376 377 @Override 378 public int bulkInsert(Uri uri, ContentValues[] allValues) { 379 if (DEBUG) { 380 Slog.v(LOG_TAG, "bulkInsert() for user: " + UserHandle.getCallingUserId()); 381 } 382 383 int insertionCount = 0; 384 final int valuesCount = allValues.length; 385 for (int i = 0; i < valuesCount; i++) { 386 ContentValues values = allValues[i]; 387 if (insert(uri, values) != null) { 388 insertionCount++; 389 } 390 } 391 392 return insertionCount; 393 } 394 395 @Override 396 public int delete(Uri uri, String where, String[] whereArgs) { 397 if (DEBUG) { 398 Slog.v(LOG_TAG, "delete() for user: " + UserHandle.getCallingUserId()); 399 } 400 401 Arguments args = new Arguments(uri, where, whereArgs, false); 402 403 // If a legacy table that is gone, done. 404 if (REMOVED_LEGACY_TABLES.contains(args.table)) { 405 return 0; 406 } 407 408 if (TextUtils.isEmpty(args.name)) { 409 return 0; 410 } 411 412 synchronized (mLock) { 413 switch (args.table) { 414 case TABLE_GLOBAL: { 415 final int userId = UserHandle.getCallingUserId(); 416 return deleteGlobalSettingLocked(args.name, userId) ? 1 : 0; 417 } 418 419 case TABLE_SECURE: { 420 final int userId = UserHandle.getCallingUserId(); 421 return deleteSecureSettingLocked(args.name, userId) ? 1 : 0; 422 } 423 424 case TABLE_SYSTEM: { 425 final int userId = UserHandle.getCallingUserId(); 426 return deleteSystemSettingLocked(args.name, userId) ? 1 : 0; 427 } 428 429 default: { 430 throw new IllegalArgumentException("Bad Uri path:" + uri); 431 } 432 } 433 } 434 } 435 436 @Override 437 public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { 438 if (DEBUG) { 439 Slog.v(LOG_TAG, "update() for user: " + UserHandle.getCallingUserId()); 440 } 441 442 Arguments args = new Arguments(uri, where, whereArgs, false); 443 444 // If a legacy table that is gone, done. 445 if (REMOVED_LEGACY_TABLES.contains(args.table)) { 446 return 0; 447 } 448 449 String value = values.getAsString(Settings.Secure.VALUE); 450 if (TextUtils.isEmpty(value)) { 451 return 0; 452 } 453 454 synchronized (mLock) { 455 switch (args.table) { 456 case TABLE_GLOBAL: { 457 final int userId = UserHandle.getCallingUserId(); 458 return updateGlobalSettingLocked(args.name, value, userId) ? 1 : 0; 459 } 460 461 case TABLE_SECURE: { 462 final int userId = UserHandle.getCallingUserId(); 463 return updateSecureSettingLocked(args.name, value, userId) ? 1 : 0; 464 } 465 466 case TABLE_SYSTEM: { 467 final int userId = UserHandle.getCallingUserId(); 468 return updateSystemSettingLocked(args.name, value, userId) ? 1 : 0; 469 } 470 471 default: { 472 throw new IllegalArgumentException("Invalid Uri path:" + uri); 473 } 474 } 475 } 476 } 477 478 @Override 479 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 480 throw new FileNotFoundException("Direct file access no longer supported; " 481 + "ringtone playback is available through android.media.Ringtone"); 482 } 483 484 @Override 485 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 486 synchronized (mLock) { 487 final long identity = Binder.clearCallingIdentity(); 488 try { 489 List<UserInfo> users = mUserManager.getUsers(true); 490 final int userCount = users.size(); 491 for (int i = 0; i < userCount; i++) { 492 UserInfo user = users.get(i); 493 dumpForUser(user.id, pw); 494 } 495 } finally { 496 Binder.restoreCallingIdentity(identity); 497 } 498 } 499 } 500 501 private void dumpForUser(int userId, PrintWriter pw) { 502 if (userId == UserHandle.USER_OWNER) { 503 pw.println("GLOBAL SETTINGS (user " + userId + ")"); 504 Cursor globalCursor = getAllGlobalSettingsLocked(ALL_COLUMNS); 505 dumpSettings(globalCursor, pw); 506 pw.println(); 507 } 508 509 pw.println("SECURE SETTINGS (user " + userId + ")"); 510 Cursor secureCursor = getAllSecureSettingsLocked(userId, ALL_COLUMNS); 511 dumpSettings(secureCursor, pw); 512 pw.println(); 513 514 pw.println("SYSTEM SETTINGS (user " + userId + ")"); 515 Cursor systemCursor = getAllSystemSettingsLocked(userId, ALL_COLUMNS); 516 dumpSettings(systemCursor, pw); 517 pw.println(); 518 } 519 520 private void dumpSettings(Cursor cursor, PrintWriter pw) { 521 if (!cursor.moveToFirst()) { 522 return; 523 } 524 525 final int idColumnIdx = cursor.getColumnIndex(Settings.NameValueTable._ID); 526 final int nameColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.NAME); 527 final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE); 528 529 do { 530 pw.append("_id:").append(cursor.getString(idColumnIdx)); 531 pw.append(" name:").append(cursor.getString(nameColumnIdx)); 532 pw.append(" value:").append(cursor.getString(valueColumnIdx)); 533 pw.println(); 534 } while (cursor.moveToNext()); 535 } 536 537 private void registerBroadcastReceivers() { 538 IntentFilter userFilter = new IntentFilter(); 539 userFilter.addAction(Intent.ACTION_USER_REMOVED); 540 userFilter.addAction(Intent.ACTION_USER_STOPPED); 541 542 getContext().registerReceiver(new BroadcastReceiver() { 543 @Override 544 public void onReceive(Context context, Intent intent) { 545 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 546 UserHandle.USER_OWNER); 547 548 switch (intent.getAction()) { 549 case Intent.ACTION_USER_REMOVED: { 550 mSettingsRegistry.removeUserStateLocked(userId, true); 551 } break; 552 553 case Intent.ACTION_USER_STOPPED: { 554 mSettingsRegistry.removeUserStateLocked(userId, false); 555 } break; 556 } 557 } 558 }, userFilter); 559 560 PackageMonitor monitor = new PackageMonitor() { 561 @Override 562 public void onPackageRemoved(String packageName, int uid) { 563 synchronized (mLock) { 564 mSettingsRegistry.onPackageRemovedLocked(packageName, 565 UserHandle.getUserId(uid)); 566 } 567 } 568 }; 569 570 // package changes 571 monitor.register(getContext(), BackgroundThread.getHandler().getLooper(), 572 UserHandle.ALL, true); 573 } 574 575 private Cursor getAllGlobalSettingsLocked(String[] projection) { 576 if (DEBUG) { 577 Slog.v(LOG_TAG, "getAllGlobalSettingsLocked()"); 578 } 579 580 // Get the settings. 581 SettingsState settingsState = mSettingsRegistry.getSettingsLocked( 582 SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER); 583 584 List<String> names = settingsState.getSettingNamesLocked(); 585 586 final int nameCount = names.size(); 587 588 String[] normalizedProjection = normalizeProjection(projection); 589 MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount); 590 591 // Anyone can get the global settings, so no security checks. 592 for (int i = 0; i < nameCount; i++) { 593 String name = names.get(i); 594 Setting setting = settingsState.getSettingLocked(name); 595 appendSettingToCursor(result, setting); 596 } 597 598 return result; 599 } 600 601 private Setting getGlobalSettingLocked(String name) { 602 if (DEBUG) { 603 Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")"); 604 } 605 606 // Get the value. 607 return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL, 608 UserHandle.USER_OWNER, name); 609 } 610 611 private boolean updateGlobalSettingLocked(String name, String value, int requestingUserId) { 612 if (DEBUG) { 613 Slog.v(LOG_TAG, "updateGlobalSettingLocked(" + name + ", " + value + ")"); 614 } 615 return mutateGlobalSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_UPDATE); 616 } 617 618 private boolean insertGlobalSettingLocked(String name, String value, int requestingUserId) { 619 if (DEBUG) { 620 Slog.v(LOG_TAG, "insertGlobalSettingLocked(" + name + ", " + value + ")"); 621 } 622 return mutateGlobalSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_INSERT); 623 } 624 625 private boolean deleteGlobalSettingLocked(String name, int requestingUserId) { 626 if (DEBUG) { 627 Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")"); 628 } 629 return mutateGlobalSettingLocked(name, null, requestingUserId, MUTATION_OPERATION_DELETE); 630 } 631 632 private boolean mutateGlobalSettingLocked(String name, String value, int requestingUserId, 633 int operation) { 634 // Make sure the caller can change the settings - treated as secure. 635 enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); 636 637 // Verify whether this operation is allowed for the calling package. 638 if (!isAppOpWriteSettingsAllowedForCallingPackage()) { 639 return false; 640 } 641 642 // Resolve the userId on whose behalf the call is made. 643 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 644 645 // If this is a setting that is currently restricted for this user, done. 646 if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId)) { 647 return false; 648 } 649 650 // Perform the mutation. 651 switch (operation) { 652 case MUTATION_OPERATION_INSERT: { 653 return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL, 654 UserHandle.USER_OWNER, name, value, getCallingPackage()); 655 } 656 657 case MUTATION_OPERATION_DELETE: { 658 return mSettingsRegistry.deleteSettingLocked( 659 SettingsRegistry.SETTINGS_TYPE_GLOBAL, 660 UserHandle.USER_OWNER, name); 661 } 662 663 case MUTATION_OPERATION_UPDATE: { 664 return mSettingsRegistry.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL, 665 UserHandle.USER_OWNER, name, value, getCallingPackage()); 666 } 667 } 668 669 return false; 670 } 671 672 private Cursor getAllSecureSettingsLocked(int userId, String[] projection) { 673 if (DEBUG) { 674 Slog.v(LOG_TAG, "getAllSecureSettings(" + userId + ")"); 675 } 676 677 // Resolve the userId on whose behalf the call is made. 678 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); 679 680 List<String> names = mSettingsRegistry.getSettingsNamesLocked( 681 SettingsRegistry.SETTINGS_TYPE_SECURE, callingUserId); 682 683 final int nameCount = names.size(); 684 685 String[] normalizedProjection = normalizeProjection(projection); 686 MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount); 687 688 for (int i = 0; i < nameCount; i++) { 689 String name = names.get(i); 690 691 // Determine the owning user as some profile settings are cloned from the parent. 692 final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); 693 694 // Special case for location (sigh). 695 if (isLocationProvidersAllowedRestricted(name, callingUserId, owningUserId)) { 696 return null; 697 } 698 699 Setting setting = mSettingsRegistry.getSettingLocked( 700 SettingsRegistry.SETTINGS_TYPE_SECURE, owningUserId, name); 701 appendSettingToCursor(result, setting); 702 } 703 704 return result; 705 } 706 707 private Setting getSecureSettingLocked(String name, int requestingUserId) { 708 if (DEBUG) { 709 Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")"); 710 } 711 712 // Resolve the userId on whose behalf the call is made. 713 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 714 715 // Determine the owning user as some profile settings are cloned from the parent. 716 final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); 717 718 // Special case for location (sigh). 719 if (isLocationProvidersAllowedRestricted(name, callingUserId, owningUserId)) { 720 return null; 721 } 722 723 // Get the value. 724 return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE, 725 owningUserId, name); 726 } 727 728 private boolean insertSecureSettingLocked(String name, String value, int requestingUserId) { 729 if (DEBUG) { 730 Slog.v(LOG_TAG, "insertSecureSettingLocked(" + name + ", " + value + ", " 731 + requestingUserId + ")"); 732 } 733 734 return mutateSecureSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_INSERT); 735 } 736 737 private boolean deleteSecureSettingLocked(String name, int requestingUserId) { 738 if (DEBUG) { 739 Slog.v(LOG_TAG, "deleteSecureSettingLocked(" + name + ", " + requestingUserId + ")"); 740 } 741 742 return mutateSecureSettingLocked(name, null, requestingUserId, MUTATION_OPERATION_DELETE); 743 } 744 745 private boolean updateSecureSettingLocked(String name, String value, int requestingUserId) { 746 if (DEBUG) { 747 Slog.v(LOG_TAG, "updateSecureSettingLocked(" + name + ", " + value + ", " 748 + requestingUserId + ")"); 749 } 750 751 return mutateSecureSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_UPDATE); 752 } 753 754 private boolean mutateSecureSettingLocked(String name, String value, int requestingUserId, 755 int operation) { 756 // Make sure the caller can change the settings. 757 enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); 758 759 // Verify whether this operation is allowed for the calling package. 760 if (!isAppOpWriteSettingsAllowedForCallingPackage()) { 761 return false; 762 } 763 764 // Resolve the userId on whose behalf the call is made. 765 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 766 767 // If this is a setting that is currently restricted for this user, done. 768 if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId)) { 769 return false; 770 } 771 772 // Determine the owning user as some profile settings are cloned from the parent. 773 final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); 774 775 // Only the owning user can change the setting. 776 if (owningUserId != callingUserId) { 777 return false; 778 } 779 780 // Special cases for location providers (sigh). 781 if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { 782 return updateLocationProvidersAllowed(value, owningUserId); 783 } 784 785 // Mutate the value. 786 switch(operation) { 787 case MUTATION_OPERATION_INSERT: { 788 return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE, 789 owningUserId, name, value, getCallingPackage()); 790 } 791 792 case MUTATION_OPERATION_DELETE: { 793 return mSettingsRegistry.deleteSettingLocked( 794 SettingsRegistry.SETTINGS_TYPE_SECURE, 795 owningUserId, name); 796 } 797 798 case MUTATION_OPERATION_UPDATE: { 799 return mSettingsRegistry.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE, 800 owningUserId, name, value, getCallingPackage()); 801 } 802 } 803 804 return false; 805 } 806 807 private Cursor getAllSystemSettingsLocked(int userId, String[] projection) { 808 if (DEBUG) { 809 Slog.v(LOG_TAG, "getAllSecureSystemLocked(" + userId + ")"); 810 } 811 812 // Resolve the userId on whose behalf the call is made. 813 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); 814 815 List<String> names = mSettingsRegistry.getSettingsNamesLocked( 816 SettingsRegistry.SETTINGS_TYPE_SYSTEM, callingUserId); 817 818 final int nameCount = names.size(); 819 820 String[] normalizedProjection = normalizeProjection(projection); 821 MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount); 822 823 for (int i = 0; i < nameCount; i++) { 824 String name = names.get(i); 825 826 // Determine the owning user as some profile settings are cloned from the parent. 827 final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name); 828 829 Setting setting = mSettingsRegistry.getSettingLocked( 830 SettingsRegistry.SETTINGS_TYPE_SYSTEM, owningUserId, name); 831 appendSettingToCursor(result, setting); 832 } 833 834 return result; 835 } 836 837 private Setting getSystemSettingLocked(String name, int requestingUserId) { 838 if (DEBUG) { 839 Slog.v(LOG_TAG, "getSystemSetting(" + name + ", " + requestingUserId + ")"); 840 } 841 842 // Resolve the userId on whose behalf the call is made. 843 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 844 845 // Determine the owning user as some profile settings are cloned from the parent. 846 final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name); 847 848 // Get the value. 849 return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM, 850 owningUserId, name); 851 } 852 853 private boolean insertSystemSettingLocked(String name, String value, int requestingUserId) { 854 if (DEBUG) { 855 Slog.v(LOG_TAG, "insertSystemSettingLocked(" + name + ", " + value + ", " 856 + requestingUserId + ")"); 857 } 858 859 return mutateSystemSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_INSERT); 860 } 861 862 private boolean deleteSystemSettingLocked(String name, int requestingUserId) { 863 if (DEBUG) { 864 Slog.v(LOG_TAG, "deleteSystemSettingLocked(" + name + ", " + requestingUserId + ")"); 865 } 866 867 return mutateSystemSettingLocked(name, null, requestingUserId, MUTATION_OPERATION_DELETE); 868 } 869 870 private boolean updateSystemSettingLocked(String name, String value, int requestingUserId) { 871 if (DEBUG) { 872 Slog.v(LOG_TAG, "updateSystemSettingLocked(" + name + ", " + value + ", " 873 + requestingUserId + ")"); 874 } 875 876 return mutateSystemSettingLocked(name, value, requestingUserId, MUTATION_OPERATION_UPDATE); 877 } 878 879 private boolean mutateSystemSettingLocked(String name, String value, int runAsUserId, 880 int operation) { 881 // Make sure the caller can change the settings. 882 enforceWritePermission(Manifest.permission.WRITE_SETTINGS); 883 884 // Verify whether this operation is allowed for the calling package. 885 if (!isAppOpWriteSettingsAllowedForCallingPackage()) { 886 return false; 887 } 888 889 // Enforce what the calling package can mutate in the system settings. 890 enforceRestrictedSystemSettingsMutationForCallingPackageLocked(operation, name); 891 892 // Resolve the userId on whose behalf the call is made. 893 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId); 894 895 // Determine the owning user as some profile settings are cloned from the parent. 896 final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name); 897 898 // Only the owning user id can change the setting. 899 if (owningUserId != callingUserId) { 900 return false; 901 } 902 903 // Mutate the value. 904 switch (operation) { 905 case MUTATION_OPERATION_INSERT: { 906 validateSystemSettingValue(name, value); 907 return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM, 908 owningUserId, name, value, getCallingPackage()); 909 } 910 911 case MUTATION_OPERATION_DELETE: { 912 return mSettingsRegistry.deleteSettingLocked( 913 SettingsRegistry.SETTINGS_TYPE_SYSTEM, 914 owningUserId, name); 915 } 916 917 case MUTATION_OPERATION_UPDATE: { 918 validateSystemSettingValue(name, value); 919 return mSettingsRegistry.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM, 920 owningUserId, name, value, getCallingPackage()); 921 } 922 } 923 924 return false; 925 } 926 927 private void validateSystemSettingValue(String name, String value) { 928 Settings.System.Validator validator = Settings.System.VALIDATORS.get(name); 929 if (validator != null && !validator.validate(value)) { 930 throw new IllegalArgumentException("Invalid value: " + value 931 + " for setting: " + name); 932 } 933 } 934 935 private boolean isLocationProvidersAllowedRestricted(String name, int callingUserId, 936 int owningUserId) { 937 // Optimization - location providers are restricted only for managed profiles. 938 if (callingUserId == owningUserId) { 939 return false; 940 } 941 if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name) 942 && mUserManager.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, 943 new UserHandle(callingUserId))) { 944 return true; 945 } 946 return false; 947 } 948 949 private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId) { 950 String restriction = sSettingToUserRestrictionMap.get(setting); 951 if (restriction == null) { 952 return false; 953 } 954 return mUserManager.hasUserRestriction(restriction, new UserHandle(userId)); 955 } 956 957 private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) { 958 return resolveOwningUserIdLocked(userId, sSecureCloneToManagedSettings, setting); 959 } 960 961 private int resolveOwningUserIdForSystemSettingLocked(int userId, String setting) { 962 return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting); 963 } 964 965 private int resolveOwningUserIdLocked(int userId, Set<String> keys, String name) { 966 final int parentId = getGroupParentLocked(userId); 967 if (parentId != userId && keys.contains(name)) { 968 return parentId; 969 } 970 return userId; 971 } 972 973 private void enforceRestrictedSystemSettingsMutationForCallingPackageLocked(int operation, 974 String name) { 975 // System/root/shell can mutate whatever secure settings they want. 976 final int callingUid = Binder.getCallingUid(); 977 if (callingUid == android.os.Process.SYSTEM_UID 978 || callingUid == Process.SHELL_UID 979 || callingUid == Process.ROOT_UID) { 980 return; 981 } 982 983 switch (operation) { 984 case MUTATION_OPERATION_INSERT: 985 // Insert updates. 986 case MUTATION_OPERATION_UPDATE: { 987 if (Settings.System.PUBLIC_SETTINGS.contains(name)) { 988 return; 989 } 990 991 // The calling package is already verified. 992 PackageInfo packageInfo = getCallingPackageInfoOrThrow(); 993 994 // Privileged apps can do whatever they want. 995 if ((packageInfo.applicationInfo.privateFlags 996 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 997 return; 998 } 999 1000 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( 1001 packageInfo.applicationInfo.targetSdkVersion, name); 1002 } break; 1003 1004 case MUTATION_OPERATION_DELETE: { 1005 if (Settings.System.PUBLIC_SETTINGS.contains(name) 1006 || Settings.System.PRIVATE_SETTINGS.contains(name)) { 1007 throw new IllegalArgumentException("You cannot delete system defined" 1008 + " secure settings."); 1009 } 1010 1011 // The calling package is already verified. 1012 PackageInfo packageInfo = getCallingPackageInfoOrThrow(); 1013 1014 // Privileged apps can do whatever they want. 1015 if ((packageInfo.applicationInfo.privateFlags & 1016 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 1017 return; 1018 } 1019 1020 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( 1021 packageInfo.applicationInfo.targetSdkVersion, name); 1022 } break; 1023 } 1024 } 1025 1026 private PackageInfo getCallingPackageInfoOrThrow() { 1027 try { 1028 return mPackageManager.getPackageInfo(getCallingPackage(), 0); 1029 } catch (PackageManager.NameNotFoundException e) { 1030 throw new IllegalStateException("Calling package doesn't exist"); 1031 } 1032 } 1033 1034 private int getGroupParentLocked(int userId) { 1035 // Most frequent use case. 1036 if (userId == UserHandle.USER_OWNER) { 1037 return userId; 1038 } 1039 // We are in the same process with the user manager and the returned 1040 // user info is a cached instance, so just look up instead of cache. 1041 final long identity = Binder.clearCallingIdentity(); 1042 try { 1043 UserInfo userInfo = mUserManager.getProfileParent(userId); 1044 return (userInfo != null) ? userInfo.id : userId; 1045 } finally { 1046 Binder.restoreCallingIdentity(identity); 1047 } 1048 } 1049 1050 private boolean isAppOpWriteSettingsAllowedForCallingPackage() { 1051 final int callingUid = Binder.getCallingUid(); 1052 1053 mAppOpsManager.checkPackage(Binder.getCallingUid(), getCallingPackage()); 1054 1055 return mAppOpsManager.noteOp(AppOpsManager.OP_WRITE_SETTINGS, callingUid, 1056 getCallingPackage()) == AppOpsManager.MODE_ALLOWED; 1057 } 1058 1059 private void enforceWritePermission(String permission) { 1060 if (getContext().checkCallingOrSelfPermission(permission) 1061 != PackageManager.PERMISSION_GRANTED) { 1062 throw new SecurityException("Permission denial: writing to settings requires:" 1063 + permission); 1064 } 1065 } 1066 1067 /* 1068 * Used to parse changes to the value of Settings.Secure.LOCATION_PROVIDERS_ALLOWED. 1069 * This setting contains a list of the currently enabled location providers. 1070 * But helper functions in android.providers.Settings can enable or disable 1071 * a single provider by using a "+" or "-" prefix before the provider name. 1072 * 1073 * @returns whether the enabled location providers changed. 1074 */ 1075 private boolean updateLocationProvidersAllowed(String value, int owningUserId) { 1076 if (TextUtils.isEmpty(value)) { 1077 return false; 1078 } 1079 1080 final char prefix = value.charAt(0); 1081 if (prefix != '+' && prefix != '-') { 1082 return false; 1083 } 1084 1085 // skip prefix 1086 value = value.substring(1); 1087 1088 Setting settingValue = getSecureSettingLocked( 1089 Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId); 1090 1091 String oldProviders = (settingValue != null) ? settingValue.getValue() : ""; 1092 1093 int index = oldProviders.indexOf(value); 1094 int end = index + value.length(); 1095 1096 // check for commas to avoid matching on partial string 1097 if (index > 0 && oldProviders.charAt(index - 1) != ',') { 1098 index = -1; 1099 } 1100 1101 // check for commas to avoid matching on partial string 1102 if (end < oldProviders.length() && oldProviders.charAt(end) != ',') { 1103 index = -1; 1104 } 1105 1106 String newProviders; 1107 1108 if (prefix == '+' && index < 0) { 1109 // append the provider to the list if not present 1110 if (oldProviders.length() == 0) { 1111 newProviders = value; 1112 } else { 1113 newProviders = oldProviders + ',' + value; 1114 } 1115 } else if (prefix == '-' && index >= 0) { 1116 // remove the provider from the list if present 1117 // remove leading or trailing comma 1118 if (index > 0) { 1119 index--; 1120 } else if (end < oldProviders.length()) { 1121 end++; 1122 } 1123 1124 newProviders = oldProviders.substring(0, index); 1125 if (end < oldProviders.length()) { 1126 newProviders += oldProviders.substring(end); 1127 } 1128 } else { 1129 // nothing changed, so no need to update the database 1130 return false; 1131 } 1132 1133 updateSecureSettingLocked(Settings.Secure.LOCATION_PROVIDERS_ALLOWED, 1134 newProviders, owningUserId); 1135 1136 return true; 1137 } 1138 1139 private void sendNotify(Uri uri, int userId) { 1140 final long identity = Binder.clearCallingIdentity(); 1141 try { 1142 getContext().getContentResolver().notifyChange(uri, null, true, userId); 1143 if (DEBUG) { 1144 Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri); 1145 } 1146 } finally { 1147 Binder.restoreCallingIdentity(identity); 1148 } 1149 } 1150 1151 private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( 1152 int targetSdkVersion, String name) { 1153 // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. 1154 if (targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) { 1155 if (Settings.System.PRIVATE_SETTINGS.contains(name)) { 1156 Slog.w(LOG_TAG, "You shouldn't not change private system settings." 1157 + " This will soon become an error."); 1158 } else { 1159 Slog.w(LOG_TAG, "You shouldn't keep your settings in the secure settings." 1160 + " This will soon become an error."); 1161 } 1162 } else { 1163 if (Settings.System.PRIVATE_SETTINGS.contains(name)) { 1164 throw new IllegalArgumentException("You cannot change private secure settings."); 1165 } else { 1166 throw new IllegalArgumentException("You cannot keep your settings in" 1167 + " the secure settings."); 1168 } 1169 } 1170 } 1171 1172 private static int resolveCallingUserIdEnforcingPermissionsLocked(int requestingUserId) { 1173 if (requestingUserId == UserHandle.getCallingUserId()) { 1174 return requestingUserId; 1175 } 1176 return ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1177 Binder.getCallingUid(), requestingUserId, false, true, 1178 "get/set setting for user", null); 1179 } 1180 1181 private static Bundle packageValueForCallResult(Setting setting) { 1182 if (setting == null) { 1183 return NULL_SETTING; 1184 } 1185 return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue()); 1186 } 1187 1188 private static int getRequestingUserId(Bundle args) { 1189 final int callingUserId = UserHandle.getCallingUserId(); 1190 return (args != null) ? args.getInt(Settings.CALL_METHOD_USER_KEY, callingUserId) 1191 : callingUserId; 1192 } 1193 1194 private static String getSettingValue(Bundle args) { 1195 return (args != null) ? args.getString(Settings.NameValueTable.VALUE) : null; 1196 } 1197 1198 private static String getValidTableOrThrow(Uri uri) { 1199 if (uri.getPathSegments().size() > 0) { 1200 String table = uri.getPathSegments().get(0); 1201 if (DatabaseHelper.isValidTable(table)) { 1202 return table; 1203 } 1204 throw new IllegalArgumentException("Bad root path: " + table); 1205 } 1206 throw new IllegalArgumentException("Invalid URI:" + uri); 1207 } 1208 1209 private static MatrixCursor packageSettingForQuery(Setting setting, String[] projection) { 1210 if (setting == null) { 1211 return new MatrixCursor(projection, 0); 1212 } 1213 MatrixCursor cursor = new MatrixCursor(projection, 1); 1214 appendSettingToCursor(cursor, setting); 1215 return cursor; 1216 } 1217 1218 private static String[] normalizeProjection(String[] projection) { 1219 if (projection == null) { 1220 return ALL_COLUMNS; 1221 } 1222 1223 final int columnCount = projection.length; 1224 for (int i = 0; i < columnCount; i++) { 1225 String column = projection[i]; 1226 if (!ArrayUtils.contains(ALL_COLUMNS, column)) { 1227 throw new IllegalArgumentException("Invalid column: " + column); 1228 } 1229 } 1230 1231 return projection; 1232 } 1233 1234 private static void appendSettingToCursor(MatrixCursor cursor, Setting setting) { 1235 final int columnCount = cursor.getColumnCount(); 1236 1237 String[] values = new String[columnCount]; 1238 1239 for (int i = 0; i < columnCount; i++) { 1240 String column = cursor.getColumnName(i); 1241 1242 switch (column) { 1243 case Settings.NameValueTable._ID: { 1244 values[i] = setting.getId(); 1245 } break; 1246 1247 case Settings.NameValueTable.NAME: { 1248 values[i] = setting.getName(); 1249 } break; 1250 1251 case Settings.NameValueTable.VALUE: { 1252 values[i] = setting.getValue(); 1253 } break; 1254 } 1255 } 1256 1257 cursor.addRow(values); 1258 } 1259 1260 private static final class Arguments { 1261 private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS = 1262 Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*"); 1263 1264 private static final Pattern WHERE_PATTERN_WITH_PARAM_IN_BRACKETS = 1265 Pattern.compile("[\\s]*\\([\\s]*name[\\s]*=[\\s]*\\?[\\s]*\\)[\\s]*"); 1266 1267 private static final Pattern WHERE_PATTERN_NO_PARAM_IN_BRACKETS = 1268 Pattern.compile("[\\s]*\\([\\s]*name[\\s]*=[\\s]*['\"].*['\"][\\s]*\\)[\\s]*"); 1269 1270 private static final Pattern WHERE_PATTERN_NO_PARAM_NO_BRACKETS = 1271 Pattern.compile("[\\s]*name[\\s]*=[\\s]*['\"].*['\"][\\s]*"); 1272 1273 public final String table; 1274 public final String name; 1275 1276 public Arguments(Uri uri, String where, String[] whereArgs, boolean supportAll) { 1277 final int segmentSize = uri.getPathSegments().size(); 1278 switch (segmentSize) { 1279 case 1: { 1280 if (where != null 1281 && (WHERE_PATTERN_WITH_PARAM_NO_BRACKETS.matcher(where).matches() 1282 || WHERE_PATTERN_WITH_PARAM_IN_BRACKETS.matcher(where).matches()) 1283 && whereArgs.length == 1) { 1284 name = whereArgs[0]; 1285 table = computeTableForSetting(uri, name); 1286 } else if (where != null 1287 && (WHERE_PATTERN_NO_PARAM_NO_BRACKETS.matcher(where).matches() 1288 || WHERE_PATTERN_NO_PARAM_IN_BRACKETS.matcher(where).matches())) { 1289 final int startIndex = Math.max(where.indexOf("'"), 1290 where.indexOf("\"")) + 1; 1291 final int endIndex = Math.max(where.lastIndexOf("'"), 1292 where.lastIndexOf("\"")); 1293 name = where.substring(startIndex, endIndex); 1294 table = computeTableForSetting(uri, name); 1295 } else if (supportAll && where == null && whereArgs == null) { 1296 name = null; 1297 table = computeTableForSetting(uri, null); 1298 } else if (uri.getPathSegments().size() == 2 1299 && where == null && whereArgs == null) { 1300 name = uri.getPathSegments().get(1); 1301 table = computeTableForSetting(uri, name); 1302 } else { 1303 EventLogTags.writeUnsupportedSettingsQuery( 1304 uri.toSafeString(), where, Arrays.toString(whereArgs)); 1305 throw new IllegalArgumentException("Only null where and args" 1306 + " or name=? where and a single arg or name='SOME_SETTING' " 1307 + "are supported uri: " + uri + " where: " + where + " args: " 1308 + Arrays.toString(whereArgs)); 1309 } 1310 } break; 1311 1312 default: { 1313 throw new IllegalArgumentException("Invalid URI: " + uri); 1314 } 1315 } 1316 } 1317 1318 public static String computeTableForSetting(Uri uri, String name) { 1319 String table = getValidTableOrThrow(uri); 1320 1321 if (name != null) { 1322 if (sSystemMovedToSecureSettings.contains(name)) { 1323 table = TABLE_SECURE; 1324 } 1325 1326 if (sSystemMovedToGlobalSettings.contains(name)) { 1327 table = TABLE_GLOBAL; 1328 } 1329 1330 if (sSecureMovedToGlobalSettings.contains(name)) { 1331 table = TABLE_GLOBAL; 1332 } 1333 1334 if (sGlobalMovedToSecureSettings.contains(name)) { 1335 table = TABLE_SECURE; 1336 } 1337 } 1338 1339 return table; 1340 } 1341 } 1342 1343 final class SettingsRegistry { 1344 private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid"; 1345 1346 private static final int SETTINGS_TYPE_GLOBAL = 0; 1347 private static final int SETTINGS_TYPE_SYSTEM = 1; 1348 private static final int SETTINGS_TYPE_SECURE = 2; 1349 1350 private static final int SETTINGS_TYPE_MASK = 0xF0000000; 1351 private static final int SETTINGS_TYPE_SHIFT = 28; 1352 1353 private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml"; 1354 private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml"; 1355 private static final String SETTINGS_FILE_SECURE = "settings_secure.xml"; 1356 1357 private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>(); 1358 1359 private final BackupManager mBackupManager; 1360 1361 public SettingsRegistry() { 1362 mBackupManager = new BackupManager(getContext()); 1363 migrateAllLegacySettingsIfNeeded(); 1364 } 1365 1366 public List<String> getSettingsNamesLocked(int type, int userId) { 1367 final int key = makeKey(type, userId); 1368 SettingsState settingsState = peekSettingsStateLocked(key); 1369 return settingsState.getSettingNamesLocked(); 1370 } 1371 1372 public SettingsState getSettingsLocked(int type, int userId) { 1373 final int key = makeKey(type, userId); 1374 return peekSettingsStateLocked(key); 1375 } 1376 1377 public void ensureSettingsForUserLocked(int userId) { 1378 // Migrate the setting for this user if needed. 1379 migrateLegacySettingsForUserIfNeededLocked(userId); 1380 1381 // Ensure global settings loaded if owner. 1382 if (userId == UserHandle.USER_OWNER) { 1383 final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER); 1384 ensureSettingsStateLocked(globalKey); 1385 } 1386 1387 // Ensure secure settings loaded. 1388 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1389 ensureSettingsStateLocked(secureKey); 1390 1391 // Make sure the secure settings have an Android id set. 1392 SettingsState secureSettings = getSettingsLocked(SETTINGS_TYPE_SECURE, userId); 1393 ensureSecureSettingAndroidIdSetLocked(secureSettings); 1394 1395 // Ensure system settings loaded. 1396 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1397 ensureSettingsStateLocked(systemKey); 1398 1399 // Upgrade the settings to the latest version. 1400 UpgradeController upgrader = new UpgradeController(userId); 1401 upgrader.upgradeIfNeededLocked(); 1402 } 1403 1404 private void ensureSettingsStateLocked(int key) { 1405 if (mSettingsStates.get(key) == null) { 1406 final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key)); 1407 SettingsState settingsState = new SettingsState(mLock, getSettingsFile(key), key, 1408 maxBytesPerPackage); 1409 mSettingsStates.put(key, settingsState); 1410 } 1411 } 1412 1413 public void removeUserStateLocked(int userId, boolean permanently) { 1414 // We always keep the global settings in memory. 1415 1416 // Nuke system settings. 1417 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1418 final SettingsState systemSettingsState = mSettingsStates.get(systemKey); 1419 if (systemSettingsState != null) { 1420 if (permanently) { 1421 mSettingsStates.remove(systemKey); 1422 systemSettingsState.destroyLocked(null); 1423 } else { 1424 systemSettingsState.destroyLocked(new Runnable() { 1425 @Override 1426 public void run() { 1427 mSettingsStates.remove(systemKey); 1428 } 1429 }); 1430 } 1431 } 1432 1433 // Nuke secure settings. 1434 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1435 final SettingsState secureSettingsState = mSettingsStates.get(secureKey); 1436 if (secureSettingsState != null) { 1437 if (permanently) { 1438 mSettingsStates.remove(secureKey); 1439 secureSettingsState.destroyLocked(null); 1440 } else { 1441 secureSettingsState.destroyLocked(new Runnable() { 1442 @Override 1443 public void run() { 1444 mSettingsStates.remove(secureKey); 1445 } 1446 }); 1447 } 1448 } 1449 } 1450 1451 public boolean insertSettingLocked(int type, int userId, String name, String value, 1452 String packageName) { 1453 final int key = makeKey(type, userId); 1454 1455 SettingsState settingsState = peekSettingsStateLocked(key); 1456 final boolean success = settingsState.insertSettingLocked(name, value, packageName); 1457 1458 if (success) { 1459 notifyForSettingsChange(key, name); 1460 } 1461 return success; 1462 } 1463 1464 public boolean deleteSettingLocked(int type, int userId, String name) { 1465 final int key = makeKey(type, userId); 1466 1467 SettingsState settingsState = peekSettingsStateLocked(key); 1468 final boolean success = settingsState.deleteSettingLocked(name); 1469 1470 if (success) { 1471 notifyForSettingsChange(key, name); 1472 } 1473 return success; 1474 } 1475 1476 public Setting getSettingLocked(int type, int userId, String name) { 1477 final int key = makeKey(type, userId); 1478 1479 SettingsState settingsState = peekSettingsStateLocked(key); 1480 return settingsState.getSettingLocked(name); 1481 } 1482 1483 public boolean updateSettingLocked(int type, int userId, String name, String value, 1484 String packageName) { 1485 final int key = makeKey(type, userId); 1486 1487 SettingsState settingsState = peekSettingsStateLocked(key); 1488 final boolean success = settingsState.updateSettingLocked(name, value, packageName); 1489 1490 if (success) { 1491 notifyForSettingsChange(key, name); 1492 } 1493 1494 return success; 1495 } 1496 1497 public void onPackageRemovedLocked(String packageName, int userId) { 1498 final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER); 1499 SettingsState globalSettings = mSettingsStates.get(globalKey); 1500 globalSettings.onPackageRemovedLocked(packageName); 1501 1502 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1503 SettingsState secureSettings = mSettingsStates.get(secureKey); 1504 secureSettings.onPackageRemovedLocked(packageName); 1505 1506 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1507 SettingsState systemSettings = mSettingsStates.get(systemKey); 1508 systemSettings.onPackageRemovedLocked(packageName); 1509 } 1510 1511 private SettingsState peekSettingsStateLocked(int key) { 1512 SettingsState settingsState = mSettingsStates.get(key); 1513 if (settingsState != null) { 1514 return settingsState; 1515 } 1516 1517 ensureSettingsForUserLocked(getUserIdFromKey(key)); 1518 return mSettingsStates.get(key); 1519 } 1520 1521 private void migrateAllLegacySettingsIfNeeded() { 1522 synchronized (mLock) { 1523 final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER); 1524 File globalFile = getSettingsFile(key); 1525 if (globalFile.exists()) { 1526 return; 1527 } 1528 1529 final long identity = Binder.clearCallingIdentity(); 1530 try { 1531 List<UserInfo> users = mUserManager.getUsers(true); 1532 1533 final int userCount = users.size(); 1534 for (int i = 0; i < userCount; i++) { 1535 final int userId = users.get(i).id; 1536 1537 DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId); 1538 SQLiteDatabase database = dbHelper.getWritableDatabase(); 1539 migrateLegacySettingsForUserLocked(dbHelper, database, userId); 1540 1541 // Upgrade to the latest version. 1542 UpgradeController upgrader = new UpgradeController(userId); 1543 upgrader.upgradeIfNeededLocked(); 1544 1545 // Drop from memory if not a running user. 1546 if (!mUserManager.isUserRunning(new UserHandle(userId))) { 1547 removeUserStateLocked(userId, false); 1548 } 1549 } 1550 } finally { 1551 Binder.restoreCallingIdentity(identity); 1552 } 1553 } 1554 } 1555 1556 private void migrateLegacySettingsForUserIfNeededLocked(int userId) { 1557 // Every user has secure settings and if no file we need to migrate. 1558 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1559 File secureFile = getSettingsFile(secureKey); 1560 if (secureFile.exists()) { 1561 return; 1562 } 1563 1564 DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId); 1565 SQLiteDatabase database = dbHelper.getWritableDatabase(); 1566 1567 migrateLegacySettingsForUserLocked(dbHelper, database, userId); 1568 } 1569 1570 private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper, 1571 SQLiteDatabase database, int userId) { 1572 // Move over the global settings if owner. 1573 if (userId == UserHandle.USER_OWNER) { 1574 final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId); 1575 ensureSettingsStateLocked(globalKey); 1576 SettingsState globalSettings = mSettingsStates.get(globalKey); 1577 migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL); 1578 globalSettings.persistSyncLocked(); 1579 } 1580 1581 // Move over the secure settings. 1582 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1583 ensureSettingsStateLocked(secureKey); 1584 SettingsState secureSettings = mSettingsStates.get(secureKey); 1585 migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE); 1586 ensureSecureSettingAndroidIdSetLocked(secureSettings); 1587 secureSettings.persistSyncLocked(); 1588 1589 // Move over the system settings. 1590 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1591 ensureSettingsStateLocked(systemKey); 1592 SettingsState systemSettings = mSettingsStates.get(systemKey); 1593 migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM); 1594 systemSettings.persistSyncLocked(); 1595 1596 // Drop the database as now all is moved and persisted. 1597 if (DROP_DATABASE_ON_MIGRATION) { 1598 dbHelper.dropDatabase(); 1599 } else { 1600 dbHelper.backupDatabase(); 1601 } 1602 } 1603 1604 private void migrateLegacySettingsLocked(SettingsState settingsState, 1605 SQLiteDatabase database, String table) { 1606 SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); 1607 queryBuilder.setTables(table); 1608 1609 Cursor cursor = queryBuilder.query(database, ALL_COLUMNS, 1610 null, null, null, null, null); 1611 1612 if (cursor == null) { 1613 return; 1614 } 1615 1616 try { 1617 if (!cursor.moveToFirst()) { 1618 return; 1619 } 1620 1621 final int nameColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.NAME); 1622 final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE); 1623 1624 settingsState.setVersionLocked(database.getVersion()); 1625 1626 while (!cursor.isAfterLast()) { 1627 String name = cursor.getString(nameColumnIdx); 1628 String value = cursor.getString(valueColumnIdx); 1629 settingsState.insertSettingLocked(name, value, 1630 SettingsState.SYSTEM_PACKAGE_NAME); 1631 cursor.moveToNext(); 1632 } 1633 } finally { 1634 cursor.close(); 1635 } 1636 } 1637 1638 private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) { 1639 Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID); 1640 1641 if (value != null) { 1642 return; 1643 } 1644 1645 final int userId = getUserIdFromKey(secureSettings.mKey); 1646 1647 final UserInfo user; 1648 final long identity = Binder.clearCallingIdentity(); 1649 try { 1650 user = mUserManager.getUserInfo(userId); 1651 } finally { 1652 Binder.restoreCallingIdentity(identity); 1653 } 1654 if (user == null) { 1655 // Can happen due to races when deleting users - treat as benign. 1656 return; 1657 } 1658 1659 String androidId = Long.toHexString(new SecureRandom().nextLong()); 1660 secureSettings.insertSettingLocked(Settings.Secure.ANDROID_ID, androidId, 1661 SettingsState.SYSTEM_PACKAGE_NAME); 1662 1663 Slog.d(LOG_TAG, "Generated and saved new ANDROID_ID [" + androidId 1664 + "] for user " + userId); 1665 1666 // Write a drop box entry if it's a restricted profile 1667 if (user.isRestricted()) { 1668 DropBoxManager dbm = (DropBoxManager) getContext().getSystemService( 1669 Context.DROPBOX_SERVICE); 1670 if (dbm != null && dbm.isTagEnabled(DROPBOX_TAG_USERLOG)) { 1671 dbm.addText(DROPBOX_TAG_USERLOG, System.currentTimeMillis() 1672 + "," + DROPBOX_TAG_USERLOG + "," + androidId + "\n"); 1673 } 1674 } 1675 } 1676 1677 private void notifyForSettingsChange(int key, String name) { 1678 // Update the system property *first*, so if someone is listening for 1679 // a notification and then using the contract class to get their data, 1680 // the system property will be updated and they'll get the new data. 1681 1682 boolean backedUpDataChanged = false; 1683 String property = null; 1684 if (isGlobalSettingsKey(key)) { 1685 property = Settings.Global.SYS_PROP_SETTING_VERSION; 1686 backedUpDataChanged = true; 1687 } else if (isSecureSettingsKey(key)) { 1688 property = Settings.Secure.SYS_PROP_SETTING_VERSION; 1689 backedUpDataChanged = true; 1690 } else if (isSystemSettingsKey(key)) { 1691 property = Settings.System.SYS_PROP_SETTING_VERSION; 1692 backedUpDataChanged = true; 1693 } 1694 1695 if (property != null) { 1696 final long version = SystemProperties.getLong(property, 0) + 1; 1697 SystemProperties.set(property, Long.toString(version)); 1698 if (DEBUG) { 1699 Slog.v(LOG_TAG, "System property " + property + "=" + version); 1700 } 1701 } 1702 1703 // Inform the backup manager about a data change 1704 if (backedUpDataChanged) { 1705 mBackupManager.dataChanged(); 1706 } 1707 1708 // Now send the notification through the content framework. 1709 1710 final int userId = getUserIdFromKey(key); 1711 Uri uri = getNotificationUriFor(key, name); 1712 1713 sendNotify(uri, userId); 1714 } 1715 1716 private int makeKey(int type, int userId) { 1717 return (type << SETTINGS_TYPE_SHIFT) | userId; 1718 } 1719 1720 private int getTypeFromKey(int key) { 1721 return key >> SETTINGS_TYPE_SHIFT; 1722 } 1723 1724 private int getUserIdFromKey(int key) { 1725 return key & ~SETTINGS_TYPE_MASK; 1726 } 1727 1728 private boolean isGlobalSettingsKey(int key) { 1729 return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL; 1730 } 1731 1732 private boolean isSystemSettingsKey(int key) { 1733 return getTypeFromKey(key) == SETTINGS_TYPE_SYSTEM; 1734 } 1735 1736 private boolean isSecureSettingsKey(int key) { 1737 return getTypeFromKey(key) == SETTINGS_TYPE_SECURE; 1738 } 1739 1740 private File getSettingsFile(int key) { 1741 if (isGlobalSettingsKey(key)) { 1742 final int userId = getUserIdFromKey(key); 1743 return new File(Environment.getUserSystemDirectory(userId), 1744 SETTINGS_FILE_GLOBAL); 1745 } else if (isSystemSettingsKey(key)) { 1746 final int userId = getUserIdFromKey(key); 1747 return new File(Environment.getUserSystemDirectory(userId), 1748 SETTINGS_FILE_SYSTEM); 1749 } else if (isSecureSettingsKey(key)) { 1750 final int userId = getUserIdFromKey(key); 1751 return new File(Environment.getUserSystemDirectory(userId), 1752 SETTINGS_FILE_SECURE); 1753 } else { 1754 throw new IllegalArgumentException("Invalid settings key:" + key); 1755 } 1756 } 1757 1758 private Uri getNotificationUriFor(int key, String name) { 1759 if (isGlobalSettingsKey(key)) { 1760 return (name != null) ? Uri.withAppendedPath(Settings.Global.CONTENT_URI, name) 1761 : Settings.Global.CONTENT_URI; 1762 } else if (isSecureSettingsKey(key)) { 1763 return (name != null) ? Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name) 1764 : Settings.Secure.CONTENT_URI; 1765 } else if (isSystemSettingsKey(key)) { 1766 return (name != null) ? Uri.withAppendedPath(Settings.System.CONTENT_URI, name) 1767 : Settings.System.CONTENT_URI; 1768 } else { 1769 throw new IllegalArgumentException("Invalid settings key:" + key); 1770 } 1771 } 1772 1773 private int getMaxBytesPerPackageForType(int type) { 1774 switch (type) { 1775 case SETTINGS_TYPE_GLOBAL: 1776 case SETTINGS_TYPE_SECURE: { 1777 return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED; 1778 } 1779 1780 default: { 1781 return SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED; 1782 } 1783 } 1784 } 1785 1786 private final class UpgradeController { 1787 private static final int SETTINGS_VERSION = 118; 1788 1789 private final int mUserId; 1790 1791 public UpgradeController(int userId) { 1792 mUserId = userId; 1793 } 1794 1795 public void upgradeIfNeededLocked() { 1796 // The version of all settings for a user is the same (all users have secure). 1797 SettingsState secureSettings = getSettingsLocked( 1798 SettingsRegistry.SETTINGS_TYPE_SECURE, mUserId); 1799 1800 // Try an update from the current state. 1801 final int oldVersion = secureSettings.getVersionLocked(); 1802 final int newVersion = SETTINGS_VERSION; 1803 1804 // If up do data - done. 1805 if (oldVersion == newVersion) { 1806 return; 1807 } 1808 1809 // Try to upgrade. 1810 final int curVersion = onUpgradeLocked(mUserId, oldVersion, newVersion); 1811 1812 // If upgrade failed start from scratch and upgrade. 1813 if (curVersion != newVersion) { 1814 // Drop state we have for this user. 1815 removeUserStateLocked(mUserId, true); 1816 1817 // Recreate the database. 1818 DatabaseHelper dbHelper = new DatabaseHelper(getContext(), mUserId); 1819 SQLiteDatabase database = dbHelper.getWritableDatabase(); 1820 dbHelper.recreateDatabase(database, newVersion, curVersion, oldVersion); 1821 1822 // Migrate the settings for this user. 1823 migrateLegacySettingsForUserLocked(dbHelper, database, mUserId); 1824 1825 // Now upgrade should work fine. 1826 onUpgradeLocked(mUserId, oldVersion, newVersion); 1827 } 1828 1829 // Set the global settings version if owner. 1830 if (mUserId == UserHandle.USER_OWNER) { 1831 SettingsState globalSettings = getSettingsLocked( 1832 SettingsRegistry.SETTINGS_TYPE_GLOBAL, mUserId); 1833 globalSettings.setVersionLocked(newVersion); 1834 } 1835 1836 // Set the secure settings version. 1837 secureSettings.setVersionLocked(newVersion); 1838 1839 // Set the system settings version. 1840 SettingsState systemSettings = getSettingsLocked( 1841 SettingsRegistry.SETTINGS_TYPE_SYSTEM, mUserId); 1842 systemSettings.setVersionLocked(newVersion); 1843 } 1844 1845 private SettingsState getGlobalSettingsLocked() { 1846 return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_OWNER); 1847 } 1848 1849 private SettingsState getSecureSettingsLocked(int userId) { 1850 return getSettingsLocked(SETTINGS_TYPE_SECURE, userId); 1851 } 1852 1853 private SettingsState getSystemSettingsLocked(int userId) { 1854 return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId); 1855 } 1856 1857 private int onUpgradeLocked(int userId, int oldVersion, int newVersion) { 1858 if (DEBUG) { 1859 Slog.w(LOG_TAG, "Upgrading settings for user: " + userId + " from version: " 1860 + oldVersion + " to version: " + newVersion); 1861 } 1862 1863 // You must perform all necessary mutations to bring the settings 1864 // for this user from the old to the new version. When you add a new 1865 // upgrade step you *must* update SETTINGS_VERSION. 1866 1867 /** 1868 * This is an example of moving a setting from secure to global. 1869 * 1870 * int currentVersion = oldVersion; 1871 * if (currentVersion == 118) { 1872 * // Remove from the secure settings. 1873 * SettingsState secureSettings = getSecureSettingsLocked(userId); 1874 * String name = "example_setting_to_move"; 1875 * String value = secureSettings.getSetting(name); 1876 * secureSettings.deleteSetting(name); 1877 * 1878 * // Add to the global settings. 1879 * SettingsState globalSettings = getGlobalSettingsLocked(); 1880 * globalSettings.insertSetting(name, value, SettingsState.SYSTEM_PACKAGE_NAME); 1881 * 1882 * // Update the current version. 1883 * currentVersion = 119; 1884 * } 1885 * 1886 * // Return the current version. 1887 * return currentVersion; 1888 */ 1889 1890 return SettingsState.VERSION_UNDEFINED; 1891 } 1892 } 1893 } 1894} 1895