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 17 package com.android.providers.settings; 18 19 import android.Manifest; 20 import android.app.ActivityManager; 21 import android.app.AppGlobals; 22 import android.app.backup.BackupManager; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.ContentProvider; 26 import android.content.ContentValues; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.IPackageManager; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.UserInfo; 35 import android.database.Cursor; 36 import android.database.MatrixCursor; 37 import android.database.sqlite.SQLiteDatabase; 38 import android.database.sqlite.SQLiteQueryBuilder; 39 import android.hardware.camera2.utils.ArrayUtils; 40 import android.media.AudioManager; 41 import android.net.Uri; 42 import android.os.Binder; 43 import android.os.Build; 44 import android.os.Bundle; 45 import android.os.Debug; 46 import android.os.DropBoxManager; 47 import android.os.Environment; 48 import android.os.Handler; 49 import android.os.HandlerThread; 50 import android.os.Looper; 51 import android.os.Message; 52 import android.os.ParcelFileDescriptor; 53 import android.os.Process; 54 import android.os.RemoteException; 55 import android.os.SELinux; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 import android.os.UserManagerInternal; 59 import android.provider.Settings; 60 import android.text.TextUtils; 61 import android.util.ArraySet; 62 import android.util.Slog; 63 import android.util.SparseArray; 64 65 import com.android.internal.annotations.GuardedBy; 66 import com.android.internal.content.PackageMonitor; 67 import com.android.internal.os.BackgroundThread; 68 import com.android.providers.settings.SettingsState.Setting; 69 import com.android.server.LocalServices; 70 import com.android.server.SystemConfig; 71 72 import java.io.File; 73 import java.io.FileDescriptor; 74 import java.io.FileNotFoundException; 75 import java.io.PrintWriter; 76 import java.security.SecureRandom; 77 import java.util.Arrays; 78 import java.util.List; 79 import java.util.Set; 80 import java.util.regex.Pattern; 81 82 /** 83 * <p> 84 * This class is a content provider that publishes the system settings. 85 * It can be accessed via the content provider APIs or via custom call 86 * commands. The latter is a bit faster and is the preferred way to access 87 * the platform settings. 88 * </p> 89 * <p> 90 * There are three settings types, global (with signature level protection 91 * and shared across users), secure (with signature permission level 92 * protection and per user), and system (with dangerous permission level 93 * protection and per user). Global settings are stored under the device owner. 94 * Each of these settings is represented by a {@link 95 * com.android.providers.settings.SettingsState} object mapped to an integer 96 * key derived from the setting type in the most significant bits and user 97 * id in the least significant bits. Settings are synchronously loaded on 98 * instantiation of a SettingsState and asynchronously persisted on mutation. 99 * Settings are stored in the user specific system directory. 100 * </p> 101 * <p> 102 * Apps targeting APIs Lollipop MR1 and lower can add custom settings entries 103 * and get a warning. Targeting higher API version prohibits this as the 104 * system settings are not a place for apps to save their state. When a package 105 * is removed the settings it added are deleted. Apps cannot delete system 106 * settings added by the platform. System settings values are validated to 107 * ensure the clients do not put bad values. Global and secure settings are 108 * changed only by trusted parties, therefore no validation is performed. Also 109 * there is a limit on the amount of app specific settings that can be added 110 * to prevent unlimited growth of the system process memory footprint. 111 * </p> 112 */ 113 @SuppressWarnings("deprecation") 114 public class SettingsProvider extends ContentProvider { 115 private static final boolean DEBUG = false; 116 117 private static final boolean DROP_DATABASE_ON_MIGRATION = !Build.IS_DEBUGGABLE; 118 119 private static final String LOG_TAG = "SettingsProvider"; 120 121 private static final String TABLE_SYSTEM = "system"; 122 private static final String TABLE_SECURE = "secure"; 123 private static final String TABLE_GLOBAL = "global"; 124 125 // Old tables no longer exist. 126 private static final String TABLE_FAVORITES = "favorites"; 127 private static final String TABLE_OLD_FAVORITES = "old_favorites"; 128 private static final String TABLE_BLUETOOTH_DEVICES = "bluetooth_devices"; 129 private static final String TABLE_BOOKMARKS = "bookmarks"; 130 private static final String TABLE_ANDROID_METADATA = "android_metadata"; 131 132 // The set of removed legacy tables. 133 private static final Set<String> REMOVED_LEGACY_TABLES = new ArraySet<>(); 134 static { 135 REMOVED_LEGACY_TABLES.add(TABLE_FAVORITES); 136 REMOVED_LEGACY_TABLES.add(TABLE_OLD_FAVORITES); 137 REMOVED_LEGACY_TABLES.add(TABLE_BLUETOOTH_DEVICES); 138 REMOVED_LEGACY_TABLES.add(TABLE_BOOKMARKS); 139 REMOVED_LEGACY_TABLES.add(TABLE_ANDROID_METADATA); 140 } 141 142 private static final int MUTATION_OPERATION_INSERT = 1; 143 private static final int MUTATION_OPERATION_DELETE = 2; 144 private static final int MUTATION_OPERATION_UPDATE = 3; 145 146 private static final String[] ALL_COLUMNS = new String[] { 147 Settings.NameValueTable._ID, 148 Settings.NameValueTable.NAME, 149 Settings.NameValueTable.VALUE 150 }; 151 152 public static final int SETTINGS_TYPE_GLOBAL = 0; 153 public static final int SETTINGS_TYPE_SYSTEM = 1; 154 public static final int SETTINGS_TYPE_SECURE = 2; 155 156 public static final int SETTINGS_TYPE_MASK = 0xF0000000; 157 public static final int SETTINGS_TYPE_SHIFT = 28; 158 159 private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair( 160 Settings.NameValueTable.VALUE, null); 161 162 // Per user secure settings that moved to the for all users global settings. 163 static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>(); 164 static { 165 Settings.Secure.getMovedToGlobalSettings(sSecureMovedToGlobalSettings); 166 } 167 168 // Per user system settings that moved to the for all users global settings. 169 static final Set<String> sSystemMovedToGlobalSettings = new ArraySet<>(); 170 static { 171 Settings.System.getMovedToGlobalSettings(sSystemMovedToGlobalSettings); 172 } 173 174 // Per user system settings that moved to the per user secure settings. 175 static final Set<String> sSystemMovedToSecureSettings = new ArraySet<>(); 176 static { 177 Settings.System.getMovedToSecureSettings(sSystemMovedToSecureSettings); 178 } 179 180 // Per all users global settings that moved to the per user secure settings. 181 static final Set<String> sGlobalMovedToSecureSettings = new ArraySet<>(); 182 static { 183 Settings.Global.getMovedToSecureSettings(sGlobalMovedToSecureSettings); 184 } 185 186 // Per user secure settings that are cloned for the managed profiles of the user. 187 private static final Set<String> sSecureCloneToManagedSettings = new ArraySet<>(); 188 static { 189 Settings.Secure.getCloneToManagedProfileSettings(sSecureCloneToManagedSettings); 190 } 191 192 // Per user system settings that are cloned for the managed profiles of the user. 193 private static final Set<String> sSystemCloneToManagedSettings = new ArraySet<>(); 194 static { 195 Settings.System.getCloneToManagedProfileSettings(sSystemCloneToManagedSettings); 196 } 197 198 private final Object mLock = new Object(); 199 200 @GuardedBy("mLock") 201 private SettingsRegistry mSettingsRegistry; 202 203 @GuardedBy("mLock") 204 private HandlerThread mHandlerThread; 205 206 // We have to call in the user manager with no lock held, 207 private volatile UserManager mUserManager; 208 209 // We have to call in the package manager with no lock held, 210 private volatile IPackageManager mPackageManager; 211 makeKey(int type, int userId)212 public static int makeKey(int type, int userId) { 213 return (type << SETTINGS_TYPE_SHIFT) | userId; 214 } 215 getTypeFromKey(int key)216 public static int getTypeFromKey(int key) { 217 return key >>> SETTINGS_TYPE_SHIFT; 218 } 219 getUserIdFromKey(int key)220 public static int getUserIdFromKey(int key) { 221 return key & ~SETTINGS_TYPE_MASK; 222 } 223 settingTypeToString(int type)224 public static String settingTypeToString(int type) { 225 switch (type) { 226 case SETTINGS_TYPE_GLOBAL: { 227 return "SETTINGS_GLOBAL"; 228 } 229 case SETTINGS_TYPE_SECURE: { 230 return "SETTINGS_SECURE"; 231 } 232 case SETTINGS_TYPE_SYSTEM: { 233 return "SETTINGS_SYSTEM"; 234 } 235 default: { 236 return "UNKNOWN"; 237 } 238 } 239 } 240 keyToString(int key)241 public static String keyToString(int key) { 242 return "Key[user=" + getUserIdFromKey(key) + ";type=" 243 + settingTypeToString(getTypeFromKey(key)) + "]"; 244 } 245 246 @Override onCreate()247 public boolean onCreate() { 248 synchronized (mLock) { 249 mUserManager = UserManager.get(getContext()); 250 mPackageManager = AppGlobals.getPackageManager(); 251 mHandlerThread = new HandlerThread(LOG_TAG, 252 Process.THREAD_PRIORITY_BACKGROUND); 253 mHandlerThread.start(); 254 mSettingsRegistry = new SettingsRegistry(); 255 } 256 registerBroadcastReceivers(); 257 startWatchingUserRestrictionChanges(); 258 return true; 259 } 260 261 @Override call(String method, String name, Bundle args)262 public Bundle call(String method, String name, Bundle args) { 263 final int requestingUserId = getRequestingUserId(args); 264 switch (method) { 265 case Settings.CALL_METHOD_GET_GLOBAL: { 266 Setting setting = getGlobalSetting(name); 267 return packageValueForCallResult(setting, isTrackingGeneration(args)); 268 } 269 270 case Settings.CALL_METHOD_GET_SECURE: { 271 Setting setting = getSecureSetting(name, requestingUserId); 272 return packageValueForCallResult(setting, isTrackingGeneration(args)); 273 } 274 275 case Settings.CALL_METHOD_GET_SYSTEM: { 276 Setting setting = getSystemSetting(name, requestingUserId); 277 return packageValueForCallResult(setting, isTrackingGeneration(args)); 278 } 279 280 case Settings.CALL_METHOD_PUT_GLOBAL: { 281 String value = getSettingValue(args); 282 insertGlobalSetting(name, value, requestingUserId, false); 283 break; 284 } 285 286 case Settings.CALL_METHOD_PUT_SECURE: { 287 String value = getSettingValue(args); 288 insertSecureSetting(name, value, requestingUserId, false); 289 break; 290 } 291 292 case Settings.CALL_METHOD_PUT_SYSTEM: { 293 String value = getSettingValue(args); 294 insertSystemSetting(name, value, requestingUserId); 295 break; 296 } 297 298 default: { 299 Slog.w(LOG_TAG, "call() with invalid method: " + method); 300 } break; 301 } 302 303 return null; 304 } 305 306 @Override getType(Uri uri)307 public String getType(Uri uri) { 308 Arguments args = new Arguments(uri, null, null, true); 309 if (TextUtils.isEmpty(args.name)) { 310 return "vnd.android.cursor.dir/" + args.table; 311 } else { 312 return "vnd.android.cursor.item/" + args.table; 313 } 314 } 315 316 @Override query(Uri uri, String[] projection, String where, String[] whereArgs, String order)317 public Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, 318 String order) { 319 if (DEBUG) { 320 Slog.v(LOG_TAG, "query() for user: " + UserHandle.getCallingUserId()); 321 } 322 323 Arguments args = new Arguments(uri, where, whereArgs, true); 324 String[] normalizedProjection = normalizeProjection(projection); 325 326 // If a legacy table that is gone, done. 327 if (REMOVED_LEGACY_TABLES.contains(args.table)) { 328 return new MatrixCursor(normalizedProjection, 0); 329 } 330 331 switch (args.table) { 332 case TABLE_GLOBAL: { 333 if (args.name != null) { 334 Setting setting = getGlobalSetting(args.name); 335 return packageSettingForQuery(setting, normalizedProjection); 336 } else { 337 return getAllGlobalSettings(projection); 338 } 339 } 340 341 case TABLE_SECURE: { 342 final int userId = UserHandle.getCallingUserId(); 343 if (args.name != null) { 344 Setting setting = getSecureSetting(args.name, userId); 345 return packageSettingForQuery(setting, normalizedProjection); 346 } else { 347 return getAllSecureSettings(userId, projection); 348 } 349 } 350 351 case TABLE_SYSTEM: { 352 final int userId = UserHandle.getCallingUserId(); 353 if (args.name != null) { 354 Setting setting = getSystemSetting(args.name, userId); 355 return packageSettingForQuery(setting, normalizedProjection); 356 } else { 357 return getAllSystemSettings(userId, projection); 358 } 359 } 360 361 default: { 362 throw new IllegalArgumentException("Invalid Uri path:" + uri); 363 } 364 } 365 } 366 367 @Override insert(Uri uri, ContentValues values)368 public Uri insert(Uri uri, ContentValues values) { 369 if (DEBUG) { 370 Slog.v(LOG_TAG, "insert() for user: " + UserHandle.getCallingUserId()); 371 } 372 373 String table = getValidTableOrThrow(uri); 374 375 // If a legacy table that is gone, done. 376 if (REMOVED_LEGACY_TABLES.contains(table)) { 377 return null; 378 } 379 380 String name = values.getAsString(Settings.Secure.NAME); 381 if (!isKeyValid(name)) { 382 return null; 383 } 384 385 String value = values.getAsString(Settings.Secure.VALUE); 386 387 switch (table) { 388 case TABLE_GLOBAL: { 389 if (insertGlobalSetting(name, value, UserHandle.getCallingUserId(), false)) { 390 return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name); 391 } 392 } break; 393 394 case TABLE_SECURE: { 395 if (insertSecureSetting(name, value, UserHandle.getCallingUserId(), false)) { 396 return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name); 397 } 398 } break; 399 400 case TABLE_SYSTEM: { 401 if (insertSystemSetting(name, value, UserHandle.getCallingUserId())) { 402 return Uri.withAppendedPath(Settings.System.CONTENT_URI, name); 403 } 404 } break; 405 406 default: { 407 throw new IllegalArgumentException("Bad Uri path:" + uri); 408 } 409 } 410 411 return null; 412 } 413 414 @Override bulkInsert(Uri uri, ContentValues[] allValues)415 public int bulkInsert(Uri uri, ContentValues[] allValues) { 416 if (DEBUG) { 417 Slog.v(LOG_TAG, "bulkInsert() for user: " + UserHandle.getCallingUserId()); 418 } 419 420 int insertionCount = 0; 421 final int valuesCount = allValues.length; 422 for (int i = 0; i < valuesCount; i++) { 423 ContentValues values = allValues[i]; 424 if (insert(uri, values) != null) { 425 insertionCount++; 426 } 427 } 428 429 return insertionCount; 430 } 431 432 @Override delete(Uri uri, String where, String[] whereArgs)433 public int delete(Uri uri, String where, String[] whereArgs) { 434 if (DEBUG) { 435 Slog.v(LOG_TAG, "delete() for user: " + UserHandle.getCallingUserId()); 436 } 437 438 Arguments args = new Arguments(uri, where, whereArgs, false); 439 440 // If a legacy table that is gone, done. 441 if (REMOVED_LEGACY_TABLES.contains(args.table)) { 442 return 0; 443 } 444 445 if (!isKeyValid(args.name)) { 446 return 0; 447 } 448 449 switch (args.table) { 450 case TABLE_GLOBAL: { 451 final int userId = UserHandle.getCallingUserId(); 452 return deleteGlobalSetting(args.name, userId, false) ? 1 : 0; 453 } 454 455 case TABLE_SECURE: { 456 final int userId = UserHandle.getCallingUserId(); 457 return deleteSecureSetting(args.name, userId, false) ? 1 : 0; 458 } 459 460 case TABLE_SYSTEM: { 461 final int userId = UserHandle.getCallingUserId(); 462 return deleteSystemSetting(args.name, userId) ? 1 : 0; 463 } 464 465 default: { 466 throw new IllegalArgumentException("Bad Uri path:" + uri); 467 } 468 } 469 } 470 471 @Override update(Uri uri, ContentValues values, String where, String[] whereArgs)472 public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { 473 if (DEBUG) { 474 Slog.v(LOG_TAG, "update() for user: " + UserHandle.getCallingUserId()); 475 } 476 477 Arguments args = new Arguments(uri, where, whereArgs, false); 478 479 // If a legacy table that is gone, done. 480 if (REMOVED_LEGACY_TABLES.contains(args.table)) { 481 return 0; 482 } 483 484 String name = values.getAsString(Settings.Secure.NAME); 485 if (!isKeyValid(name)) { 486 return 0; 487 } 488 String value = values.getAsString(Settings.Secure.VALUE); 489 490 switch (args.table) { 491 case TABLE_GLOBAL: { 492 final int userId = UserHandle.getCallingUserId(); 493 return updateGlobalSetting(args.name, value, userId, false) ? 1 : 0; 494 } 495 496 case TABLE_SECURE: { 497 final int userId = UserHandle.getCallingUserId(); 498 return updateSecureSetting(args.name, value, userId, false) ? 1 : 0; 499 } 500 501 case TABLE_SYSTEM: { 502 final int userId = UserHandle.getCallingUserId(); 503 return updateSystemSetting(args.name, value, userId) ? 1 : 0; 504 } 505 506 default: { 507 throw new IllegalArgumentException("Invalid Uri path:" + uri); 508 } 509 } 510 } 511 512 @Override openFile(Uri uri, String mode)513 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 514 final String cacheName; 515 if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) { 516 cacheName = Settings.System.RINGTONE_CACHE; 517 } else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) { 518 cacheName = Settings.System.NOTIFICATION_SOUND_CACHE; 519 } else if (Settings.System.ALARM_ALERT_CACHE_URI.equals(uri)) { 520 cacheName = Settings.System.ALARM_ALERT_CACHE; 521 } else { 522 throw new FileNotFoundException("Direct file access no longer supported; " 523 + "ringtone playback is available through android.media.Ringtone"); 524 } 525 526 final File cacheFile = new File( 527 getRingtoneCacheDir(UserHandle.getCallingUserId()), cacheName); 528 return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode)); 529 } 530 getRingtoneCacheDir(int userId)531 private File getRingtoneCacheDir(int userId) { 532 final File cacheDir = new File(Environment.getDataSystemDeDirectory(userId), "ringtones"); 533 cacheDir.mkdir(); 534 SELinux.restorecon(cacheDir); 535 return cacheDir; 536 } 537 538 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)539 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 540 synchronized (mLock) { 541 final long identity = Binder.clearCallingIdentity(); 542 try { 543 List<UserInfo> users = mUserManager.getUsers(true); 544 final int userCount = users.size(); 545 for (int i = 0; i < userCount; i++) { 546 UserInfo user = users.get(i); 547 dumpForUser(user.id, pw); 548 } 549 } finally { 550 Binder.restoreCallingIdentity(identity); 551 } 552 } 553 } 554 dumpForUser(int userId, PrintWriter pw)555 private void dumpForUser(int userId, PrintWriter pw) { 556 if (userId == UserHandle.USER_SYSTEM) { 557 pw.println("GLOBAL SETTINGS (user " + userId + ")"); 558 Cursor globalCursor = getAllGlobalSettings(ALL_COLUMNS); 559 dumpSettings(globalCursor, pw); 560 pw.println(); 561 } 562 563 pw.println("SECURE SETTINGS (user " + userId + ")"); 564 Cursor secureCursor = getAllSecureSettings(userId, ALL_COLUMNS); 565 dumpSettings(secureCursor, pw); 566 pw.println(); 567 568 pw.println("SYSTEM SETTINGS (user " + userId + ")"); 569 Cursor systemCursor = getAllSystemSettings(userId, ALL_COLUMNS); 570 dumpSettings(systemCursor, pw); 571 pw.println(); 572 } 573 dumpSettings(Cursor cursor, PrintWriter pw)574 private void dumpSettings(Cursor cursor, PrintWriter pw) { 575 if (cursor == null || !cursor.moveToFirst()) { 576 return; 577 } 578 579 final int idColumnIdx = cursor.getColumnIndex(Settings.NameValueTable._ID); 580 final int nameColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.NAME); 581 final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE); 582 583 do { 584 pw.append("_id:").append(toDumpString(cursor.getString(idColumnIdx))); 585 pw.append(" name:").append(toDumpString(cursor.getString(nameColumnIdx))); 586 pw.append(" value:").append(toDumpString(cursor.getString(valueColumnIdx))); 587 pw.println(); 588 } while (cursor.moveToNext()); 589 } 590 toDumpString(String s)591 private static String toDumpString(String s) { 592 if (s != null) { 593 return s; 594 } 595 return "{null}"; 596 } 597 registerBroadcastReceivers()598 private void registerBroadcastReceivers() { 599 IntentFilter userFilter = new IntentFilter(); 600 userFilter.addAction(Intent.ACTION_USER_REMOVED); 601 userFilter.addAction(Intent.ACTION_USER_STOPPED); 602 603 getContext().registerReceiver(new BroadcastReceiver() { 604 @Override 605 public void onReceive(Context context, Intent intent) { 606 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 607 UserHandle.USER_SYSTEM); 608 609 switch (intent.getAction()) { 610 case Intent.ACTION_USER_REMOVED: { 611 synchronized (mLock) { 612 mSettingsRegistry.removeUserStateLocked(userId, true); 613 } 614 } break; 615 616 case Intent.ACTION_USER_STOPPED: { 617 synchronized (mLock) { 618 mSettingsRegistry.removeUserStateLocked(userId, false); 619 } 620 } break; 621 } 622 } 623 }, userFilter); 624 625 PackageMonitor monitor = new PackageMonitor() { 626 @Override 627 public void onPackageRemoved(String packageName, int uid) { 628 synchronized (mLock) { 629 mSettingsRegistry.onPackageRemovedLocked(packageName, 630 UserHandle.getUserId(uid)); 631 } 632 } 633 }; 634 635 // package changes 636 monitor.register(getContext(), BackgroundThread.getHandler().getLooper(), 637 UserHandle.ALL, true); 638 } 639 startWatchingUserRestrictionChanges()640 private void startWatchingUserRestrictionChanges() { 641 // TODO: The current design of settings looking different based on user restrictions 642 // should be reworked to keep them separate and system code should check the setting 643 // first followed by checking the user restriction before performing an operation. 644 UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class); 645 userManager.addUserRestrictionsListener((int userId, Bundle newRestrictions, 646 Bundle prevRestrictions) -> { 647 // We are changing the settings affected by restrictions to their current 648 // value with a forced update to ensure that all cross profile dependencies 649 // are taken into account. Also make sure the settings update to.. the same 650 // value passes the security checks, so clear binder calling id. 651 if (newRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION) 652 != prevRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION)) { 653 final long identity = Binder.clearCallingIdentity(); 654 try { 655 synchronized (mLock) { 656 Setting setting = getSecureSetting( 657 Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId); 658 updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED, 659 setting != null ? setting.getValue() : null, userId, true); 660 } 661 } finally { 662 Binder.restoreCallingIdentity(identity); 663 } 664 } 665 if (newRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) 666 != prevRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) { 667 final long identity = Binder.clearCallingIdentity(); 668 try { 669 synchronized (mLock) { 670 Setting setting = getGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS); 671 updateGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS, 672 setting != null ? setting.getValue() : null, userId, true); 673 } 674 } finally { 675 Binder.restoreCallingIdentity(identity); 676 } 677 } 678 if (newRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES) 679 != prevRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES)) { 680 final long identity = Binder.clearCallingIdentity(); 681 try { 682 synchronized (mLock) { 683 Setting setting = getGlobalSetting(Settings.Global.ADB_ENABLED); 684 updateGlobalSetting(Settings.Global.ADB_ENABLED, 685 setting != null ? setting.getValue() : null, userId, true); 686 } 687 } finally { 688 Binder.restoreCallingIdentity(identity); 689 } 690 } 691 if (newRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS) 692 != prevRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS)) { 693 final long identity = Binder.clearCallingIdentity(); 694 try { 695 synchronized (mLock) { 696 Setting enable = getGlobalSetting( 697 Settings.Global.PACKAGE_VERIFIER_ENABLE); 698 updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE, 699 enable != null ? enable.getValue() : null, userId, true); 700 Setting include = getGlobalSetting( 701 Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB); 702 updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 703 include != null ? include.getValue() : null, userId, true); 704 } 705 } finally { 706 Binder.restoreCallingIdentity(identity); 707 } 708 } 709 if (newRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) 710 != prevRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { 711 final long identity = Binder.clearCallingIdentity(); 712 try { 713 synchronized (mLock) { 714 Setting setting = getGlobalSetting( 715 Settings.Global.PREFERRED_NETWORK_MODE); 716 updateGlobalSetting(Settings.Global.PREFERRED_NETWORK_MODE, 717 setting != null ? setting.getValue() : null, userId, true); 718 } 719 } finally { 720 Binder.restoreCallingIdentity(identity); 721 } 722 } 723 }); 724 } 725 getAllGlobalSettings(String[] projection)726 private Cursor getAllGlobalSettings(String[] projection) { 727 if (DEBUG) { 728 Slog.v(LOG_TAG, "getAllGlobalSettings()"); 729 } 730 731 synchronized (mLock) { 732 // Get the settings. 733 SettingsState settingsState = mSettingsRegistry.getSettingsLocked( 734 SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); 735 736 List<String> names = settingsState.getSettingNamesLocked(); 737 738 final int nameCount = names.size(); 739 740 String[] normalizedProjection = normalizeProjection(projection); 741 MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount); 742 743 // Anyone can get the global settings, so no security checks. 744 for (int i = 0; i < nameCount; i++) { 745 String name = names.get(i); 746 Setting setting = settingsState.getSettingLocked(name); 747 appendSettingToCursor(result, setting); 748 } 749 750 return result; 751 } 752 } 753 getGlobalSetting(String name)754 private Setting getGlobalSetting(String name) { 755 if (DEBUG) { 756 Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")"); 757 } 758 759 // Get the value. 760 synchronized (mLock) { 761 return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL, 762 UserHandle.USER_SYSTEM, name); 763 } 764 } 765 updateGlobalSetting(String name, String value, int requestingUserId, boolean forceNotify)766 private boolean updateGlobalSetting(String name, String value, int requestingUserId, 767 boolean forceNotify) { 768 if (DEBUG) { 769 Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")"); 770 } 771 return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE, 772 forceNotify); 773 } 774 insertGlobalSetting(String name, String value, int requestingUserId, boolean forceNotify)775 private boolean insertGlobalSetting(String name, String value, int requestingUserId, 776 boolean forceNotify) { 777 if (DEBUG) { 778 Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ")"); 779 } 780 return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT, 781 forceNotify); 782 } 783 deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify)784 private boolean deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify) { 785 if (DEBUG) { 786 Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")"); 787 } 788 return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE, 789 forceNotify); 790 } 791 mutateGlobalSetting(String name, String value, int requestingUserId, int operation, boolean forceNotify)792 private boolean mutateGlobalSetting(String name, String value, int requestingUserId, 793 int operation, boolean forceNotify) { 794 // Make sure the caller can change the settings - treated as secure. 795 enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); 796 797 // Resolve the userId on whose behalf the call is made. 798 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 799 800 // If this is a setting that is currently restricted for this user, do not allow 801 // unrestricting changes. 802 if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) { 803 return false; 804 } 805 806 // Perform the mutation. 807 synchronized (mLock) { 808 switch (operation) { 809 case MUTATION_OPERATION_INSERT: { 810 return mSettingsRegistry 811 .insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, 812 name, value, getCallingPackage(), forceNotify); 813 } 814 815 case MUTATION_OPERATION_DELETE: { 816 return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL, 817 UserHandle.USER_SYSTEM, name, forceNotify); 818 } 819 820 case MUTATION_OPERATION_UPDATE: { 821 return mSettingsRegistry 822 .updateSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, 823 name, value, getCallingPackage(), forceNotify); 824 } 825 } 826 } 827 828 return false; 829 } 830 getAllSecureSettings(int userId, String[] projection)831 private Cursor getAllSecureSettings(int userId, String[] projection) { 832 if (DEBUG) { 833 Slog.v(LOG_TAG, "getAllSecureSettings(" + userId + ")"); 834 } 835 836 // Resolve the userId on whose behalf the call is made. 837 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); 838 839 synchronized (mLock) { 840 List<String> names = mSettingsRegistry.getSettingsNamesLocked( 841 SETTINGS_TYPE_SECURE, callingUserId); 842 843 final int nameCount = names.size(); 844 845 String[] normalizedProjection = normalizeProjection(projection); 846 MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount); 847 848 for (int i = 0; i < nameCount; i++) { 849 String name = names.get(i); 850 // Determine the owning user as some profile settings are cloned from the parent. 851 final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, 852 name); 853 854 // Special case for location (sigh). 855 if (isLocationProvidersAllowedRestricted(name, callingUserId, owningUserId)) { 856 continue; 857 } 858 859 Setting setting = mSettingsRegistry.getSettingLocked( 860 SETTINGS_TYPE_SECURE, owningUserId, name); 861 appendSettingToCursor(result, setting); 862 } 863 864 return result; 865 } 866 } 867 getSecureSetting(String name, int requestingUserId)868 private Setting getSecureSetting(String name, int requestingUserId) { 869 if (DEBUG) { 870 Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")"); 871 } 872 873 // Resolve the userId on whose behalf the call is made. 874 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 875 876 // Determine the owning user as some profile settings are cloned from the parent. 877 final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); 878 879 // Special case for location (sigh). 880 if (isLocationProvidersAllowedRestricted(name, callingUserId, owningUserId)) { 881 return mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_SECURE, 882 owningUserId).getNullSetting(); 883 } 884 885 // Get the value. 886 synchronized (mLock) { 887 return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, 888 owningUserId, name); 889 } 890 } 891 insertSecureSetting(String name, String value, int requestingUserId, boolean forceNotify)892 private boolean insertSecureSetting(String name, String value, int requestingUserId, 893 boolean forceNotify) { 894 if (DEBUG) { 895 Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", " 896 + requestingUserId + ")"); 897 } 898 899 return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT, 900 forceNotify); 901 } 902 deleteSecureSetting(String name, int requestingUserId, boolean forceNotify)903 private boolean deleteSecureSetting(String name, int requestingUserId, boolean forceNotify) { 904 if (DEBUG) { 905 Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + ")"); 906 } 907 908 return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE, 909 forceNotify); 910 } 911 updateSecureSetting(String name, String value, int requestingUserId, boolean forceNotify)912 private boolean updateSecureSetting(String name, String value, int requestingUserId, 913 boolean forceNotify) { 914 if (DEBUG) { 915 Slog.v(LOG_TAG, "updateSecureSetting(" + name + ", " + value + ", " 916 + requestingUserId + ")"); 917 } 918 919 return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE, 920 forceNotify); 921 } 922 mutateSecureSetting(String name, String value, int requestingUserId, int operation, boolean forceNotify)923 private boolean mutateSecureSetting(String name, String value, int requestingUserId, 924 int operation, boolean forceNotify) { 925 // Make sure the caller can change the settings. 926 enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS); 927 928 // Resolve the userId on whose behalf the call is made. 929 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 930 931 // If this is a setting that is currently restricted for this user, do not allow 932 // unrestricting changes. 933 if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) { 934 return false; 935 } 936 937 // Determine the owning user as some profile settings are cloned from the parent. 938 final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); 939 940 // Only the owning user can change the setting. 941 if (owningUserId != callingUserId) { 942 return false; 943 } 944 945 // Special cases for location providers (sigh). 946 if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { 947 return updateLocationProvidersAllowedLocked(value, owningUserId, forceNotify); 948 } 949 950 // Mutate the value. 951 synchronized (mLock) { 952 switch (operation) { 953 case MUTATION_OPERATION_INSERT: { 954 return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, 955 owningUserId, name, value, getCallingPackage(), forceNotify); 956 } 957 958 case MUTATION_OPERATION_DELETE: { 959 return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE, 960 owningUserId, name, forceNotify); 961 } 962 963 case MUTATION_OPERATION_UPDATE: { 964 return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE, 965 owningUserId, name, value, getCallingPackage(), forceNotify); 966 } 967 } 968 } 969 970 return false; 971 } 972 getAllSystemSettings(int userId, String[] projection)973 private Cursor getAllSystemSettings(int userId, String[] projection) { 974 if (DEBUG) { 975 Slog.v(LOG_TAG, "getAllSecureSystem(" + userId + ")"); 976 } 977 978 // Resolve the userId on whose behalf the call is made. 979 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); 980 981 synchronized (mLock) { 982 List<String> names = mSettingsRegistry.getSettingsNamesLocked( 983 SETTINGS_TYPE_SYSTEM, callingUserId); 984 985 final int nameCount = names.size(); 986 987 String[] normalizedProjection = normalizeProjection(projection); 988 MatrixCursor result = new MatrixCursor(normalizedProjection, nameCount); 989 990 for (int i = 0; i < nameCount; i++) { 991 String name = names.get(i); 992 993 // Determine the owning user as some profile settings are cloned from the parent. 994 final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, 995 name); 996 997 Setting setting = mSettingsRegistry.getSettingLocked( 998 SETTINGS_TYPE_SYSTEM, owningUserId, name); 999 appendSettingToCursor(result, setting); 1000 } 1001 1002 return result; 1003 } 1004 } 1005 getSystemSetting(String name, int requestingUserId)1006 private Setting getSystemSetting(String name, int requestingUserId) { 1007 if (DEBUG) { 1008 Slog.v(LOG_TAG, "getSystemSetting(" + name + ", " + requestingUserId + ")"); 1009 } 1010 1011 // Resolve the userId on whose behalf the call is made. 1012 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); 1013 1014 // Determine the owning user as some profile settings are cloned from the parent. 1015 final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name); 1016 1017 // Get the value. 1018 synchronized (mLock) { 1019 return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId, name); 1020 } 1021 } 1022 insertSystemSetting(String name, String value, int requestingUserId)1023 private boolean insertSystemSetting(String name, String value, int requestingUserId) { 1024 if (DEBUG) { 1025 Slog.v(LOG_TAG, "insertSystemSetting(" + name + ", " + value + ", " 1026 + requestingUserId + ")"); 1027 } 1028 1029 return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT); 1030 } 1031 deleteSystemSetting(String name, int requestingUserId)1032 private boolean deleteSystemSetting(String name, int requestingUserId) { 1033 if (DEBUG) { 1034 Slog.v(LOG_TAG, "deleteSystemSetting(" + name + ", " + requestingUserId + ")"); 1035 } 1036 1037 return mutateSystemSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE); 1038 } 1039 updateSystemSetting(String name, String value, int requestingUserId)1040 private boolean updateSystemSetting(String name, String value, int requestingUserId) { 1041 if (DEBUG) { 1042 Slog.v(LOG_TAG, "updateSystemSetting(" + name + ", " + value + ", " 1043 + requestingUserId + ")"); 1044 } 1045 1046 return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE); 1047 } 1048 mutateSystemSetting(String name, String value, int runAsUserId, int operation)1049 private boolean mutateSystemSetting(String name, String value, int runAsUserId, 1050 int operation) { 1051 if (!hasWriteSecureSettingsPermission()) { 1052 // If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whether this 1053 // operation is allowed for the calling package through appops. 1054 if (!Settings.checkAndNoteWriteSettingsOperation(getContext(), 1055 Binder.getCallingUid(), getCallingPackage(), true)) { 1056 return false; 1057 } 1058 } 1059 1060 // Resolve the userId on whose behalf the call is made. 1061 final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId); 1062 1063 // Enforce what the calling package can mutate the system settings. 1064 enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name, callingUserId); 1065 1066 // Determine the owning user as some profile settings are cloned from the parent. 1067 final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name); 1068 1069 // Only the owning user id can change the setting. 1070 if (owningUserId != callingUserId) { 1071 return false; 1072 } 1073 1074 // Invalidate any relevant cache files 1075 String cacheName = null; 1076 if (Settings.System.RINGTONE.equals(name)) { 1077 cacheName = Settings.System.RINGTONE_CACHE; 1078 } else if (Settings.System.NOTIFICATION_SOUND.equals(name)) { 1079 cacheName = Settings.System.NOTIFICATION_SOUND_CACHE; 1080 } else if (Settings.System.ALARM_ALERT.equals(name)) { 1081 cacheName = Settings.System.ALARM_ALERT_CACHE; 1082 } 1083 if (cacheName != null) { 1084 final File cacheFile = new File( 1085 getRingtoneCacheDir(UserHandle.getCallingUserId()), cacheName); 1086 cacheFile.delete(); 1087 } 1088 1089 // Mutate the value. 1090 synchronized (mLock) { 1091 switch (operation) { 1092 case MUTATION_OPERATION_INSERT: { 1093 validateSystemSettingValue(name, value); 1094 return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM, 1095 owningUserId, name, value, getCallingPackage(), false); 1096 } 1097 1098 case MUTATION_OPERATION_DELETE: { 1099 return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM, 1100 owningUserId, name, false); 1101 } 1102 1103 case MUTATION_OPERATION_UPDATE: { 1104 validateSystemSettingValue(name, value); 1105 return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM, 1106 owningUserId, name, value, getCallingPackage(), false); 1107 } 1108 } 1109 1110 return false; 1111 } 1112 } 1113 hasWriteSecureSettingsPermission()1114 private boolean hasWriteSecureSettingsPermission() { 1115 // Write secure settings is a more protected permission. If caller has it we are good. 1116 if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) 1117 == PackageManager.PERMISSION_GRANTED) { 1118 return true; 1119 } 1120 1121 return false; 1122 } 1123 validateSystemSettingValue(String name, String value)1124 private void validateSystemSettingValue(String name, String value) { 1125 Settings.System.Validator validator = Settings.System.VALIDATORS.get(name); 1126 if (validator != null && !validator.validate(value)) { 1127 throw new IllegalArgumentException("Invalid value: " + value 1128 + " for setting: " + name); 1129 } 1130 } 1131 isLocationProvidersAllowedRestricted(String name, int callingUserId, int owningUserId)1132 private boolean isLocationProvidersAllowedRestricted(String name, int callingUserId, 1133 int owningUserId) { 1134 // Optimization - location providers are restricted only for managed profiles. 1135 if (callingUserId == owningUserId) { 1136 return false; 1137 } 1138 if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name) 1139 && mUserManager.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, 1140 new UserHandle(callingUserId))) { 1141 return true; 1142 } 1143 return false; 1144 } 1145 1146 /** 1147 * Checks whether changing a setting to a value is prohibited by the corresponding user 1148 * restriction. 1149 * 1150 * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction( 1151 * Context, int, String, boolean)}, which should be in sync with this method. 1152 * 1153 * @return true if the change is prohibited, false if the change is allowed. 1154 */ isGlobalOrSecureSettingRestrictedForUser(String setting, int userId, String value)1155 private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId, 1156 String value) { 1157 String restriction; 1158 switch (setting) { 1159 case Settings.Secure.LOCATION_MODE: 1160 // Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED 1161 // in android.provider.Settings.Secure.putStringForUser(), so we shouldn't come 1162 // here normally, but we still protect it here from a direct provider write. 1163 if (String.valueOf(Settings.Secure.LOCATION_MODE_OFF).equals(value)) return false; 1164 restriction = UserManager.DISALLOW_SHARE_LOCATION; 1165 break; 1166 1167 case Settings.Secure.LOCATION_PROVIDERS_ALLOWED: 1168 // See SettingsProvider.updateLocationProvidersAllowedLocked. "-" is to disable 1169 // a provider, which should be allowed even if the user restriction is set. 1170 if (value != null && value.startsWith("-")) return false; 1171 restriction = UserManager.DISALLOW_SHARE_LOCATION; 1172 break; 1173 1174 case Settings.Secure.INSTALL_NON_MARKET_APPS: 1175 if ("0".equals(value)) return false; 1176 restriction = UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES; 1177 break; 1178 1179 case Settings.Global.ADB_ENABLED: 1180 if ("0".equals(value)) return false; 1181 restriction = UserManager.DISALLOW_DEBUGGING_FEATURES; 1182 break; 1183 1184 case Settings.Global.PACKAGE_VERIFIER_ENABLE: 1185 case Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB: 1186 if ("1".equals(value)) return false; 1187 restriction = UserManager.ENSURE_VERIFY_APPS; 1188 break; 1189 1190 case Settings.Global.PREFERRED_NETWORK_MODE: 1191 restriction = UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS; 1192 break; 1193 1194 default: 1195 if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) { 1196 if ("0".equals(value)) return false; 1197 restriction = UserManager.DISALLOW_DATA_ROAMING; 1198 break; 1199 } 1200 return false; 1201 } 1202 1203 return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId)); 1204 } 1205 resolveOwningUserIdForSecureSettingLocked(int userId, String setting)1206 private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) { 1207 return resolveOwningUserIdLocked(userId, sSecureCloneToManagedSettings, setting); 1208 } 1209 resolveOwningUserIdForSystemSettingLocked(int userId, String setting)1210 private int resolveOwningUserIdForSystemSettingLocked(int userId, String setting) { 1211 return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting); 1212 } 1213 resolveOwningUserIdLocked(int userId, Set<String> keys, String name)1214 private int resolveOwningUserIdLocked(int userId, Set<String> keys, String name) { 1215 final int parentId = getGroupParentLocked(userId); 1216 if (parentId != userId && keys.contains(name)) { 1217 return parentId; 1218 } 1219 return userId; 1220 } 1221 enforceRestrictedSystemSettingsMutationForCallingPackage(int operation, String name, int userId)1222 private void enforceRestrictedSystemSettingsMutationForCallingPackage(int operation, 1223 String name, int userId) { 1224 // System/root/shell can mutate whatever secure settings they want. 1225 final int callingUid = Binder.getCallingUid(); 1226 if (callingUid == android.os.Process.SYSTEM_UID 1227 || callingUid == Process.SHELL_UID 1228 || callingUid == Process.ROOT_UID) { 1229 return; 1230 } 1231 1232 switch (operation) { 1233 case MUTATION_OPERATION_INSERT: 1234 // Insert updates. 1235 case MUTATION_OPERATION_UPDATE: { 1236 if (Settings.System.PUBLIC_SETTINGS.contains(name)) { 1237 return; 1238 } 1239 1240 // The calling package is already verified. 1241 PackageInfo packageInfo = getCallingPackageInfoOrThrow(userId); 1242 1243 // Privileged apps can do whatever they want. 1244 if ((packageInfo.applicationInfo.privateFlags 1245 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 1246 return; 1247 } 1248 1249 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( 1250 packageInfo.applicationInfo.targetSdkVersion, name); 1251 } break; 1252 1253 case MUTATION_OPERATION_DELETE: { 1254 if (Settings.System.PUBLIC_SETTINGS.contains(name) 1255 || Settings.System.PRIVATE_SETTINGS.contains(name)) { 1256 throw new IllegalArgumentException("You cannot delete system defined" 1257 + " secure settings."); 1258 } 1259 1260 // The calling package is already verified. 1261 PackageInfo packageInfo = getCallingPackageInfoOrThrow(userId); 1262 1263 // Privileged apps can do whatever they want. 1264 if ((packageInfo.applicationInfo.privateFlags & 1265 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { 1266 return; 1267 } 1268 1269 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( 1270 packageInfo.applicationInfo.targetSdkVersion, name); 1271 } break; 1272 } 1273 } 1274 getCallingPackageInfoOrThrow(int userId)1275 private PackageInfo getCallingPackageInfoOrThrow(int userId) { 1276 try { 1277 PackageInfo packageInfo = mPackageManager.getPackageInfo( 1278 getCallingPackage(), 0, userId); 1279 if (packageInfo != null) { 1280 return packageInfo; 1281 } 1282 } catch (RemoteException e) { 1283 /* ignore */ 1284 } 1285 throw new IllegalStateException("Calling package doesn't exist"); 1286 } 1287 getGroupParentLocked(int userId)1288 private int getGroupParentLocked(int userId) { 1289 // Most frequent use case. 1290 if (userId == UserHandle.USER_SYSTEM) { 1291 return userId; 1292 } 1293 // We are in the same process with the user manager and the returned 1294 // user info is a cached instance, so just look up instead of cache. 1295 final long identity = Binder.clearCallingIdentity(); 1296 try { 1297 // Just a lookup and not reentrant, so holding a lock is fine. 1298 UserInfo userInfo = mUserManager.getProfileParent(userId); 1299 return (userInfo != null) ? userInfo.id : userId; 1300 } finally { 1301 Binder.restoreCallingIdentity(identity); 1302 } 1303 } 1304 enforceWritePermission(String permission)1305 private void enforceWritePermission(String permission) { 1306 if (getContext().checkCallingOrSelfPermission(permission) 1307 != PackageManager.PERMISSION_GRANTED) { 1308 throw new SecurityException("Permission denial: writing to settings requires:" 1309 + permission); 1310 } 1311 } 1312 1313 /* 1314 * Used to parse changes to the value of Settings.Secure.LOCATION_PROVIDERS_ALLOWED. 1315 * This setting contains a list of the currently enabled location providers. 1316 * But helper functions in android.providers.Settings can enable or disable 1317 * a single provider by using a "+" or "-" prefix before the provider name. 1318 * 1319 * <p>See also {@link #isGlobalOrSecureSettingRestrictedForUser()}. If DISALLOW_SHARE_LOCATION 1320 * is set, the said method will only allow values with the "-" prefix. 1321 * 1322 * @returns whether the enabled location providers changed. 1323 */ updateLocationProvidersAllowedLocked(String value, int owningUserId, boolean forceNotify)1324 private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId, 1325 boolean forceNotify) { 1326 if (TextUtils.isEmpty(value)) { 1327 return false; 1328 } 1329 1330 final char prefix = value.charAt(0); 1331 if (prefix != '+' && prefix != '-') { 1332 if (forceNotify) { 1333 final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId); 1334 mSettingsRegistry.notifyForSettingsChange(key, 1335 Settings.Secure.LOCATION_PROVIDERS_ALLOWED); 1336 } 1337 return false; 1338 } 1339 1340 // skip prefix 1341 value = value.substring(1); 1342 1343 Setting settingValue = getSecureSetting( 1344 Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId); 1345 1346 String oldProviders = (settingValue != null) ? settingValue.getValue() : ""; 1347 1348 int index = oldProviders.indexOf(value); 1349 int end = index + value.length(); 1350 1351 // check for commas to avoid matching on partial string 1352 if (index > 0 && oldProviders.charAt(index - 1) != ',') { 1353 index = -1; 1354 } 1355 1356 // check for commas to avoid matching on partial string 1357 if (end < oldProviders.length() && oldProviders.charAt(end) != ',') { 1358 index = -1; 1359 } 1360 1361 String newProviders; 1362 1363 if (prefix == '+' && index < 0) { 1364 // append the provider to the list if not present 1365 if (oldProviders.length() == 0) { 1366 newProviders = value; 1367 } else { 1368 newProviders = oldProviders + ',' + value; 1369 } 1370 } else if (prefix == '-' && index >= 0) { 1371 // remove the provider from the list if present 1372 // remove leading or trailing comma 1373 if (index > 0) { 1374 index--; 1375 } else if (end < oldProviders.length()) { 1376 end++; 1377 } 1378 1379 newProviders = oldProviders.substring(0, index); 1380 if (end < oldProviders.length()) { 1381 newProviders += oldProviders.substring(end); 1382 } 1383 } else { 1384 // nothing changed, so no need to update the database 1385 if (forceNotify) { 1386 final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId); 1387 mSettingsRegistry.notifyForSettingsChange(key, 1388 Settings.Secure.LOCATION_PROVIDERS_ALLOWED); 1389 } 1390 return false; 1391 } 1392 1393 return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, 1394 owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders, 1395 getCallingPackage(), forceNotify); 1396 } 1397 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( int targetSdkVersion, String name)1398 private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( 1399 int targetSdkVersion, String name) { 1400 // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. 1401 if (targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) { 1402 if (Settings.System.PRIVATE_SETTINGS.contains(name)) { 1403 Slog.w(LOG_TAG, "You shouldn't not change private system settings." 1404 + " This will soon become an error."); 1405 } else { 1406 Slog.w(LOG_TAG, "You shouldn't keep your settings in the secure settings." 1407 + " This will soon become an error."); 1408 } 1409 } else { 1410 if (Settings.System.PRIVATE_SETTINGS.contains(name)) { 1411 throw new IllegalArgumentException("You cannot change private secure settings."); 1412 } else { 1413 throw new IllegalArgumentException("You cannot keep your settings in" 1414 + " the secure settings."); 1415 } 1416 } 1417 } 1418 resolveCallingUserIdEnforcingPermissionsLocked(int requestingUserId)1419 private static int resolveCallingUserIdEnforcingPermissionsLocked(int requestingUserId) { 1420 if (requestingUserId == UserHandle.getCallingUserId()) { 1421 return requestingUserId; 1422 } 1423 return ActivityManager.handleIncomingUser(Binder.getCallingPid(), 1424 Binder.getCallingUid(), requestingUserId, false, true, 1425 "get/set setting for user", null); 1426 } 1427 packageValueForCallResult(Setting setting, boolean trackingGeneration)1428 private Bundle packageValueForCallResult(Setting setting, 1429 boolean trackingGeneration) { 1430 if (!trackingGeneration) { 1431 if (setting.isNull()) { 1432 return NULL_SETTING_BUNDLE; 1433 } 1434 return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue()); 1435 } 1436 Bundle result = new Bundle(); 1437 result.putString(Settings.NameValueTable.VALUE, 1438 !setting.isNull() ? setting.getValue() : null); 1439 mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getkey()); 1440 return result; 1441 } 1442 getRequestingUserId(Bundle args)1443 private static int getRequestingUserId(Bundle args) { 1444 final int callingUserId = UserHandle.getCallingUserId(); 1445 return (args != null) ? args.getInt(Settings.CALL_METHOD_USER_KEY, callingUserId) 1446 : callingUserId; 1447 } 1448 isTrackingGeneration(Bundle args)1449 private boolean isTrackingGeneration(Bundle args) { 1450 return args != null && args.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY); 1451 } 1452 getSettingValue(Bundle args)1453 private static String getSettingValue(Bundle args) { 1454 return (args != null) ? args.getString(Settings.NameValueTable.VALUE) : null; 1455 } 1456 getValidTableOrThrow(Uri uri)1457 private static String getValidTableOrThrow(Uri uri) { 1458 if (uri.getPathSegments().size() > 0) { 1459 String table = uri.getPathSegments().get(0); 1460 if (DatabaseHelper.isValidTable(table)) { 1461 return table; 1462 } 1463 throw new IllegalArgumentException("Bad root path: " + table); 1464 } 1465 throw new IllegalArgumentException("Invalid URI:" + uri); 1466 } 1467 packageSettingForQuery(Setting setting, String[] projection)1468 private static MatrixCursor packageSettingForQuery(Setting setting, String[] projection) { 1469 if (setting.isNull()) { 1470 return new MatrixCursor(projection, 0); 1471 } 1472 MatrixCursor cursor = new MatrixCursor(projection, 1); 1473 appendSettingToCursor(cursor, setting); 1474 return cursor; 1475 } 1476 normalizeProjection(String[] projection)1477 private static String[] normalizeProjection(String[] projection) { 1478 if (projection == null) { 1479 return ALL_COLUMNS; 1480 } 1481 1482 final int columnCount = projection.length; 1483 for (int i = 0; i < columnCount; i++) { 1484 String column = projection[i]; 1485 if (!ArrayUtils.contains(ALL_COLUMNS, column)) { 1486 throw new IllegalArgumentException("Invalid column: " + column); 1487 } 1488 } 1489 1490 return projection; 1491 } 1492 appendSettingToCursor(MatrixCursor cursor, Setting setting)1493 private static void appendSettingToCursor(MatrixCursor cursor, Setting setting) { 1494 if (setting.isNull()) { 1495 return; 1496 } 1497 final int columnCount = cursor.getColumnCount(); 1498 1499 String[] values = new String[columnCount]; 1500 1501 for (int i = 0; i < columnCount; i++) { 1502 String column = cursor.getColumnName(i); 1503 1504 switch (column) { 1505 case Settings.NameValueTable._ID: { 1506 values[i] = setting.getId(); 1507 } break; 1508 1509 case Settings.NameValueTable.NAME: { 1510 values[i] = setting.getName(); 1511 } break; 1512 1513 case Settings.NameValueTable.VALUE: { 1514 values[i] = setting.getValue(); 1515 } break; 1516 } 1517 } 1518 1519 cursor.addRow(values); 1520 } 1521 isKeyValid(String key)1522 private static boolean isKeyValid(String key) { 1523 return !(TextUtils.isEmpty(key) || SettingsState.isBinary(key)); 1524 } 1525 1526 private static final class Arguments { 1527 private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS = 1528 Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*"); 1529 1530 private static final Pattern WHERE_PATTERN_WITH_PARAM_IN_BRACKETS = 1531 Pattern.compile("[\\s]*\\([\\s]*name[\\s]*=[\\s]*\\?[\\s]*\\)[\\s]*"); 1532 1533 private static final Pattern WHERE_PATTERN_NO_PARAM_IN_BRACKETS = 1534 Pattern.compile("[\\s]*\\([\\s]*name[\\s]*=[\\s]*['\"].*['\"][\\s]*\\)[\\s]*"); 1535 1536 private static final Pattern WHERE_PATTERN_NO_PARAM_NO_BRACKETS = 1537 Pattern.compile("[\\s]*name[\\s]*=[\\s]*['\"].*['\"][\\s]*"); 1538 1539 public final String table; 1540 public final String name; 1541 Arguments(Uri uri, String where, String[] whereArgs, boolean supportAll)1542 public Arguments(Uri uri, String where, String[] whereArgs, boolean supportAll) { 1543 final int segmentSize = uri.getPathSegments().size(); 1544 switch (segmentSize) { 1545 case 1: { 1546 if (where != null 1547 && (WHERE_PATTERN_WITH_PARAM_NO_BRACKETS.matcher(where).matches() 1548 || WHERE_PATTERN_WITH_PARAM_IN_BRACKETS.matcher(where).matches()) 1549 && whereArgs.length == 1) { 1550 name = whereArgs[0]; 1551 table = computeTableForSetting(uri, name); 1552 return; 1553 } else if (where != null 1554 && (WHERE_PATTERN_NO_PARAM_NO_BRACKETS.matcher(where).matches() 1555 || WHERE_PATTERN_NO_PARAM_IN_BRACKETS.matcher(where).matches())) { 1556 final int startIndex = Math.max(where.indexOf("'"), 1557 where.indexOf("\"")) + 1; 1558 final int endIndex = Math.max(where.lastIndexOf("'"), 1559 where.lastIndexOf("\"")); 1560 name = where.substring(startIndex, endIndex); 1561 table = computeTableForSetting(uri, name); 1562 return; 1563 } else if (supportAll && where == null && whereArgs == null) { 1564 name = null; 1565 table = computeTableForSetting(uri, null); 1566 return; 1567 } 1568 } break; 1569 1570 case 2: { 1571 if (where == null && whereArgs == null) { 1572 name = uri.getPathSegments().get(1); 1573 table = computeTableForSetting(uri, name); 1574 return; 1575 } 1576 } break; 1577 } 1578 1579 EventLogTags.writeUnsupportedSettingsQuery( 1580 uri.toSafeString(), where, Arrays.toString(whereArgs)); 1581 String message = String.format( "Supported SQL:\n" 1582 + " uri content://some_table/some_property with null where and where args\n" 1583 + " uri content://some_table with query name=? and single name as arg\n" 1584 + " uri content://some_table with query name=some_name and null args\n" 1585 + " but got - uri:%1s, where:%2s whereArgs:%3s", uri, where, 1586 Arrays.toString(whereArgs)); 1587 throw new IllegalArgumentException(message); 1588 } 1589 computeTableForSetting(Uri uri, String name)1590 private static String computeTableForSetting(Uri uri, String name) { 1591 String table = getValidTableOrThrow(uri); 1592 1593 if (name != null) { 1594 if (sSystemMovedToSecureSettings.contains(name)) { 1595 table = TABLE_SECURE; 1596 } 1597 1598 if (sSystemMovedToGlobalSettings.contains(name)) { 1599 table = TABLE_GLOBAL; 1600 } 1601 1602 if (sSecureMovedToGlobalSettings.contains(name)) { 1603 table = TABLE_GLOBAL; 1604 } 1605 1606 if (sGlobalMovedToSecureSettings.contains(name)) { 1607 table = TABLE_SECURE; 1608 } 1609 } 1610 1611 return table; 1612 } 1613 } 1614 1615 final class SettingsRegistry { 1616 private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid"; 1617 1618 private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml"; 1619 private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml"; 1620 private static final String SETTINGS_FILE_SECURE = "settings_secure.xml"; 1621 1622 private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>(); 1623 1624 private GenerationRegistry mGenerationRegistry; 1625 1626 private final Handler mHandler; 1627 1628 private final BackupManager mBackupManager; 1629 SettingsRegistry()1630 public SettingsRegistry() { 1631 mHandler = new MyHandler(getContext().getMainLooper()); 1632 mGenerationRegistry = new GenerationRegistry(mLock); 1633 mBackupManager = new BackupManager(getContext()); 1634 migrateAllLegacySettingsIfNeeded(); 1635 } 1636 getSettingsNamesLocked(int type, int userId)1637 public List<String> getSettingsNamesLocked(int type, int userId) { 1638 final int key = makeKey(type, userId); 1639 SettingsState settingsState = peekSettingsStateLocked(key); 1640 return settingsState.getSettingNamesLocked(); 1641 } 1642 getSettingsLocked(int type, int userId)1643 public SettingsState getSettingsLocked(int type, int userId) { 1644 final int key = makeKey(type, userId); 1645 return peekSettingsStateLocked(key); 1646 } 1647 ensureSettingsForUserLocked(int userId)1648 public void ensureSettingsForUserLocked(int userId) { 1649 // Migrate the setting for this user if needed. 1650 migrateLegacySettingsForUserIfNeededLocked(userId); 1651 1652 // Ensure global settings loaded if owner. 1653 if (userId == UserHandle.USER_SYSTEM) { 1654 final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); 1655 ensureSettingsStateLocked(globalKey); 1656 } 1657 1658 // Ensure secure settings loaded. 1659 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1660 ensureSettingsStateLocked(secureKey); 1661 1662 // Make sure the secure settings have an Android id set. 1663 SettingsState secureSettings = getSettingsLocked(SETTINGS_TYPE_SECURE, userId); 1664 ensureSecureSettingAndroidIdSetLocked(secureSettings); 1665 1666 // Ensure system settings loaded. 1667 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1668 ensureSettingsStateLocked(systemKey); 1669 1670 // Upgrade the settings to the latest version. 1671 UpgradeController upgrader = new UpgradeController(userId); 1672 upgrader.upgradeIfNeededLocked(); 1673 } 1674 ensureSettingsStateLocked(int key)1675 private void ensureSettingsStateLocked(int key) { 1676 if (mSettingsStates.get(key) == null) { 1677 final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key)); 1678 SettingsState settingsState = new SettingsState(mLock, getSettingsFile(key), key, 1679 maxBytesPerPackage, mHandlerThread.getLooper()); 1680 mSettingsStates.put(key, settingsState); 1681 } 1682 } 1683 removeUserStateLocked(int userId, boolean permanently)1684 public void removeUserStateLocked(int userId, boolean permanently) { 1685 // We always keep the global settings in memory. 1686 1687 // Nuke system settings. 1688 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1689 final SettingsState systemSettingsState = mSettingsStates.get(systemKey); 1690 if (systemSettingsState != null) { 1691 if (permanently) { 1692 mSettingsStates.remove(systemKey); 1693 systemSettingsState.destroyLocked(null); 1694 } else { 1695 systemSettingsState.destroyLocked(new Runnable() { 1696 @Override 1697 public void run() { 1698 mSettingsStates.remove(systemKey); 1699 } 1700 }); 1701 } 1702 } 1703 1704 // Nuke secure settings. 1705 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1706 final SettingsState secureSettingsState = mSettingsStates.get(secureKey); 1707 if (secureSettingsState != null) { 1708 if (permanently) { 1709 mSettingsStates.remove(secureKey); 1710 secureSettingsState.destroyLocked(null); 1711 } else { 1712 secureSettingsState.destroyLocked(new Runnable() { 1713 @Override 1714 public void run() { 1715 mSettingsStates.remove(secureKey); 1716 } 1717 }); 1718 } 1719 } 1720 1721 // Nuke generation tracking data 1722 mGenerationRegistry.onUserRemoved(userId); 1723 } 1724 insertSettingLocked(int type, int userId, String name, String value, String packageName, boolean forceNotify)1725 public boolean insertSettingLocked(int type, int userId, String name, String value, 1726 String packageName, boolean forceNotify) { 1727 final int key = makeKey(type, userId); 1728 1729 SettingsState settingsState = peekSettingsStateLocked(key); 1730 final boolean success = settingsState.insertSettingLocked(name, value, packageName); 1731 1732 if (forceNotify || success) { 1733 notifyForSettingsChange(key, name); 1734 } 1735 return success; 1736 } 1737 deleteSettingLocked(int type, int userId, String name, boolean forceNotify)1738 public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify) { 1739 final int key = makeKey(type, userId); 1740 1741 SettingsState settingsState = peekSettingsStateLocked(key); 1742 final boolean success = settingsState.deleteSettingLocked(name); 1743 1744 if (forceNotify || success) { 1745 notifyForSettingsChange(key, name); 1746 } 1747 return success; 1748 } 1749 getSettingLocked(int type, int userId, String name)1750 public Setting getSettingLocked(int type, int userId, String name) { 1751 final int key = makeKey(type, userId); 1752 1753 SettingsState settingsState = peekSettingsStateLocked(key); 1754 return settingsState.getSettingLocked(name); 1755 } 1756 updateSettingLocked(int type, int userId, String name, String value, String packageName, boolean forceNotify)1757 public boolean updateSettingLocked(int type, int userId, String name, String value, 1758 String packageName, boolean forceNotify) { 1759 final int key = makeKey(type, userId); 1760 1761 SettingsState settingsState = peekSettingsStateLocked(key); 1762 final boolean success = settingsState.updateSettingLocked(name, value, packageName); 1763 1764 if (forceNotify || success) { 1765 notifyForSettingsChange(key, name); 1766 } 1767 1768 return success; 1769 } 1770 onPackageRemovedLocked(String packageName, int userId)1771 public void onPackageRemovedLocked(String packageName, int userId) { 1772 // Global and secure settings are signature protected. Apps signed 1773 // by the platform certificate are generally not uninstalled and 1774 // the main exception is tests. We trust components signed 1775 // by the platform certificate and do not do a clean up after them. 1776 1777 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1778 SettingsState systemSettings = mSettingsStates.get(systemKey); 1779 if (systemSettings != null) { 1780 systemSettings.onPackageRemovedLocked(packageName); 1781 } 1782 } 1783 peekSettingsStateLocked(int key)1784 private SettingsState peekSettingsStateLocked(int key) { 1785 SettingsState settingsState = mSettingsStates.get(key); 1786 if (settingsState != null) { 1787 return settingsState; 1788 } 1789 1790 ensureSettingsForUserLocked(getUserIdFromKey(key)); 1791 return mSettingsStates.get(key); 1792 } 1793 migrateAllLegacySettingsIfNeeded()1794 private void migrateAllLegacySettingsIfNeeded() { 1795 synchronized (mLock) { 1796 final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); 1797 File globalFile = getSettingsFile(key); 1798 if (globalFile.exists()) { 1799 return; 1800 } 1801 1802 final long identity = Binder.clearCallingIdentity(); 1803 try { 1804 List<UserInfo> users = mUserManager.getUsers(true); 1805 1806 final int userCount = users.size(); 1807 for (int i = 0; i < userCount; i++) { 1808 final int userId = users.get(i).id; 1809 1810 DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId); 1811 SQLiteDatabase database = dbHelper.getWritableDatabase(); 1812 migrateLegacySettingsForUserLocked(dbHelper, database, userId); 1813 1814 // Upgrade to the latest version. 1815 UpgradeController upgrader = new UpgradeController(userId); 1816 upgrader.upgradeIfNeededLocked(); 1817 1818 // Drop from memory if not a running user. 1819 if (!mUserManager.isUserRunning(new UserHandle(userId))) { 1820 removeUserStateLocked(userId, false); 1821 } 1822 } 1823 } finally { 1824 Binder.restoreCallingIdentity(identity); 1825 } 1826 } 1827 } 1828 migrateLegacySettingsForUserIfNeededLocked(int userId)1829 private void migrateLegacySettingsForUserIfNeededLocked(int userId) { 1830 // Every user has secure settings and if no file we need to migrate. 1831 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1832 File secureFile = getSettingsFile(secureKey); 1833 if (secureFile.exists()) { 1834 return; 1835 } 1836 1837 DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId); 1838 SQLiteDatabase database = dbHelper.getWritableDatabase(); 1839 1840 migrateLegacySettingsForUserLocked(dbHelper, database, userId); 1841 } 1842 migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper, SQLiteDatabase database, int userId)1843 private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper, 1844 SQLiteDatabase database, int userId) { 1845 // Move over the system settings. 1846 final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 1847 ensureSettingsStateLocked(systemKey); 1848 SettingsState systemSettings = mSettingsStates.get(systemKey); 1849 migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM); 1850 systemSettings.persistSyncLocked(); 1851 1852 // Move over the secure settings. 1853 // Do this after System settings, since this is the first thing we check when deciding 1854 // to skip over migration from db to xml for a secondary user. 1855 final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); 1856 ensureSettingsStateLocked(secureKey); 1857 SettingsState secureSettings = mSettingsStates.get(secureKey); 1858 migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE); 1859 ensureSecureSettingAndroidIdSetLocked(secureSettings); 1860 secureSettings.persistSyncLocked(); 1861 1862 // Move over the global settings if owner. 1863 // Do this last, since this is the first thing we check when deciding 1864 // to skip over migration from db to xml for owner user. 1865 if (userId == UserHandle.USER_SYSTEM) { 1866 final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId); 1867 ensureSettingsStateLocked(globalKey); 1868 SettingsState globalSettings = mSettingsStates.get(globalKey); 1869 migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL); 1870 globalSettings.persistSyncLocked(); 1871 } 1872 1873 // Drop the database as now all is moved and persisted. 1874 if (DROP_DATABASE_ON_MIGRATION) { 1875 dbHelper.dropDatabase(); 1876 } else { 1877 dbHelper.backupDatabase(); 1878 } 1879 } 1880 migrateLegacySettingsLocked(SettingsState settingsState, SQLiteDatabase database, String table)1881 private void migrateLegacySettingsLocked(SettingsState settingsState, 1882 SQLiteDatabase database, String table) { 1883 SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); 1884 queryBuilder.setTables(table); 1885 1886 Cursor cursor = queryBuilder.query(database, ALL_COLUMNS, 1887 null, null, null, null, null); 1888 1889 if (cursor == null) { 1890 return; 1891 } 1892 1893 try { 1894 if (!cursor.moveToFirst()) { 1895 return; 1896 } 1897 1898 final int nameColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.NAME); 1899 final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE); 1900 1901 settingsState.setVersionLocked(database.getVersion()); 1902 1903 while (!cursor.isAfterLast()) { 1904 String name = cursor.getString(nameColumnIdx); 1905 String value = cursor.getString(valueColumnIdx); 1906 settingsState.insertSettingLocked(name, value, 1907 SettingsState.SYSTEM_PACKAGE_NAME); 1908 cursor.moveToNext(); 1909 } 1910 } finally { 1911 cursor.close(); 1912 } 1913 } 1914 ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings)1915 private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) { 1916 Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID); 1917 1918 if (!value.isNull()) { 1919 return; 1920 } 1921 1922 final int userId = getUserIdFromKey(secureSettings.mKey); 1923 1924 final UserInfo user; 1925 final long identity = Binder.clearCallingIdentity(); 1926 try { 1927 user = mUserManager.getUserInfo(userId); 1928 } finally { 1929 Binder.restoreCallingIdentity(identity); 1930 } 1931 if (user == null) { 1932 // Can happen due to races when deleting users - treat as benign. 1933 return; 1934 } 1935 1936 String androidId = Long.toHexString(new SecureRandom().nextLong()); 1937 secureSettings.insertSettingLocked(Settings.Secure.ANDROID_ID, androidId, 1938 SettingsState.SYSTEM_PACKAGE_NAME); 1939 1940 Slog.d(LOG_TAG, "Generated and saved new ANDROID_ID [" + androidId 1941 + "] for user " + userId); 1942 1943 // Write a drop box entry if it's a restricted profile 1944 if (user.isRestricted()) { 1945 DropBoxManager dbm = (DropBoxManager) getContext().getSystemService( 1946 Context.DROPBOX_SERVICE); 1947 if (dbm != null && dbm.isTagEnabled(DROPBOX_TAG_USERLOG)) { 1948 dbm.addText(DROPBOX_TAG_USERLOG, System.currentTimeMillis() 1949 + "," + DROPBOX_TAG_USERLOG + "," + androidId + "\n"); 1950 } 1951 } 1952 } 1953 notifyForSettingsChange(int key, String name)1954 private void notifyForSettingsChange(int key, String name) { 1955 final int userId = getUserIdFromKey(key); 1956 Uri uri = getNotificationUriFor(key, name); 1957 1958 mGenerationRegistry.incrementGeneration(key); 1959 1960 mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, 1961 userId, 0, uri).sendToTarget(); 1962 1963 if (isSecureSettingsKey(key)) { 1964 maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name, 1965 sSecureCloneToManagedSettings); 1966 } else if (isSystemSettingsKey(key)) { 1967 maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name, 1968 sSystemCloneToManagedSettings); 1969 } 1970 1971 mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget(); 1972 } 1973 maybeNotifyProfiles(int type, int userId, Uri uri, String name, Set<String> keysCloned)1974 private void maybeNotifyProfiles(int type, int userId, Uri uri, String name, 1975 Set<String> keysCloned) { 1976 if (keysCloned.contains(name)) { 1977 for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) { 1978 // the notification for userId has already been sent. 1979 if (profileId != userId) { 1980 mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, 1981 profileId, 0, uri).sendToTarget(); 1982 final int key = makeKey(type, profileId); 1983 mGenerationRegistry.incrementGeneration(key); 1984 1985 mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget(); 1986 } 1987 } 1988 } 1989 } 1990 isGlobalSettingsKey(int key)1991 private boolean isGlobalSettingsKey(int key) { 1992 return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL; 1993 } 1994 isSystemSettingsKey(int key)1995 private boolean isSystemSettingsKey(int key) { 1996 return getTypeFromKey(key) == SETTINGS_TYPE_SYSTEM; 1997 } 1998 isSecureSettingsKey(int key)1999 private boolean isSecureSettingsKey(int key) { 2000 return getTypeFromKey(key) == SETTINGS_TYPE_SECURE; 2001 } 2002 getSettingsFile(int key)2003 private File getSettingsFile(int key) { 2004 if (isGlobalSettingsKey(key)) { 2005 final int userId = getUserIdFromKey(key); 2006 return new File(Environment.getUserSystemDirectory(userId), 2007 SETTINGS_FILE_GLOBAL); 2008 } else if (isSystemSettingsKey(key)) { 2009 final int userId = getUserIdFromKey(key); 2010 return new File(Environment.getUserSystemDirectory(userId), 2011 SETTINGS_FILE_SYSTEM); 2012 } else if (isSecureSettingsKey(key)) { 2013 final int userId = getUserIdFromKey(key); 2014 return new File(Environment.getUserSystemDirectory(userId), 2015 SETTINGS_FILE_SECURE); 2016 } else { 2017 throw new IllegalArgumentException("Invalid settings key:" + key); 2018 } 2019 } 2020 getNotificationUriFor(int key, String name)2021 private Uri getNotificationUriFor(int key, String name) { 2022 if (isGlobalSettingsKey(key)) { 2023 return (name != null) ? Uri.withAppendedPath(Settings.Global.CONTENT_URI, name) 2024 : Settings.Global.CONTENT_URI; 2025 } else if (isSecureSettingsKey(key)) { 2026 return (name != null) ? Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name) 2027 : Settings.Secure.CONTENT_URI; 2028 } else if (isSystemSettingsKey(key)) { 2029 return (name != null) ? Uri.withAppendedPath(Settings.System.CONTENT_URI, name) 2030 : Settings.System.CONTENT_URI; 2031 } else { 2032 throw new IllegalArgumentException("Invalid settings key:" + key); 2033 } 2034 } 2035 getMaxBytesPerPackageForType(int type)2036 private int getMaxBytesPerPackageForType(int type) { 2037 switch (type) { 2038 case SETTINGS_TYPE_GLOBAL: 2039 case SETTINGS_TYPE_SECURE: { 2040 return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED; 2041 } 2042 2043 default: { 2044 return SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED; 2045 } 2046 } 2047 } 2048 2049 private final class MyHandler extends Handler { 2050 private static final int MSG_NOTIFY_URI_CHANGED = 1; 2051 private static final int MSG_NOTIFY_DATA_CHANGED = 2; 2052 MyHandler(Looper looper)2053 public MyHandler(Looper looper) { 2054 super(looper); 2055 } 2056 2057 @Override handleMessage(Message msg)2058 public void handleMessage(Message msg) { 2059 switch (msg.what) { 2060 case MSG_NOTIFY_URI_CHANGED: { 2061 final int userId = msg.arg1; 2062 Uri uri = (Uri) msg.obj; 2063 getContext().getContentResolver().notifyChange(uri, null, true, userId); 2064 if (DEBUG) { 2065 Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri); 2066 } 2067 } break; 2068 2069 case MSG_NOTIFY_DATA_CHANGED: { 2070 mBackupManager.dataChanged(); 2071 } break; 2072 } 2073 } 2074 } 2075 2076 private final class UpgradeController { 2077 private static final int SETTINGS_VERSION = 127; 2078 2079 private final int mUserId; 2080 UpgradeController(int userId)2081 public UpgradeController(int userId) { 2082 mUserId = userId; 2083 } 2084 upgradeIfNeededLocked()2085 public void upgradeIfNeededLocked() { 2086 // The version of all settings for a user is the same (all users have secure). 2087 SettingsState secureSettings = getSettingsLocked( 2088 SETTINGS_TYPE_SECURE, mUserId); 2089 2090 // Try an update from the current state. 2091 final int oldVersion = secureSettings.getVersionLocked(); 2092 final int newVersion = SETTINGS_VERSION; 2093 2094 // If up do date - done. 2095 if (oldVersion == newVersion) { 2096 return; 2097 } 2098 2099 // Try to upgrade. 2100 final int curVersion = onUpgradeLocked(mUserId, oldVersion, newVersion); 2101 2102 // If upgrade failed start from scratch and upgrade. 2103 if (curVersion != newVersion) { 2104 // Drop state we have for this user. 2105 removeUserStateLocked(mUserId, true); 2106 2107 // Recreate the database. 2108 DatabaseHelper dbHelper = new DatabaseHelper(getContext(), mUserId); 2109 SQLiteDatabase database = dbHelper.getWritableDatabase(); 2110 dbHelper.recreateDatabase(database, newVersion, curVersion, oldVersion); 2111 2112 // Migrate the settings for this user. 2113 migrateLegacySettingsForUserLocked(dbHelper, database, mUserId); 2114 2115 // Now upgrade should work fine. 2116 onUpgradeLocked(mUserId, oldVersion, newVersion); 2117 } 2118 2119 // Set the global settings version if owner. 2120 if (mUserId == UserHandle.USER_SYSTEM) { 2121 SettingsState globalSettings = getSettingsLocked( 2122 SETTINGS_TYPE_GLOBAL, mUserId); 2123 globalSettings.setVersionLocked(newVersion); 2124 } 2125 2126 // Set the secure settings version. 2127 secureSettings.setVersionLocked(newVersion); 2128 2129 // Set the system settings version. 2130 SettingsState systemSettings = getSettingsLocked( 2131 SETTINGS_TYPE_SYSTEM, mUserId); 2132 systemSettings.setVersionLocked(newVersion); 2133 } 2134 getGlobalSettingsLocked()2135 private SettingsState getGlobalSettingsLocked() { 2136 return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); 2137 } 2138 getSecureSettingsLocked(int userId)2139 private SettingsState getSecureSettingsLocked(int userId) { 2140 return getSettingsLocked(SETTINGS_TYPE_SECURE, userId); 2141 } 2142 getSystemSettingsLocked(int userId)2143 private SettingsState getSystemSettingsLocked(int userId) { 2144 return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId); 2145 } 2146 2147 /** 2148 * You must perform all necessary mutations to bring the settings 2149 * for this user from the old to the new version. When you add a new 2150 * upgrade step you *must* update SETTINGS_VERSION. 2151 * 2152 * This is an example of moving a setting from secure to global. 2153 * 2154 * // v119: Example settings changes. 2155 * if (currentVersion == 118) { 2156 * if (userId == UserHandle.USER_OWNER) { 2157 * // Remove from the secure settings. 2158 * SettingsState secureSettings = getSecureSettingsLocked(userId); 2159 * String name = "example_setting_to_move"; 2160 * String value = secureSettings.getSetting(name); 2161 * secureSettings.deleteSetting(name); 2162 * 2163 * // Add to the global settings. 2164 * SettingsState globalSettings = getGlobalSettingsLocked(); 2165 * globalSettings.insertSetting(name, value, SettingsState.SYSTEM_PACKAGE_NAME); 2166 * } 2167 * 2168 * // Update the current version. 2169 * currentVersion = 119; 2170 * } 2171 */ onUpgradeLocked(int userId, int oldVersion, int newVersion)2172 private int onUpgradeLocked(int userId, int oldVersion, int newVersion) { 2173 if (DEBUG) { 2174 Slog.w(LOG_TAG, "Upgrading settings for user: " + userId + " from version: " 2175 + oldVersion + " to version: " + newVersion); 2176 } 2177 2178 int currentVersion = oldVersion; 2179 2180 // v119: Reset zen + ringer mode. 2181 if (currentVersion == 118) { 2182 if (userId == UserHandle.USER_SYSTEM) { 2183 final SettingsState globalSettings = getGlobalSettingsLocked(); 2184 globalSettings.updateSettingLocked(Settings.Global.ZEN_MODE, 2185 Integer.toString(Settings.Global.ZEN_MODE_OFF), 2186 SettingsState.SYSTEM_PACKAGE_NAME); 2187 globalSettings.updateSettingLocked(Settings.Global.MODE_RINGER, 2188 Integer.toString(AudioManager.RINGER_MODE_NORMAL), 2189 SettingsState.SYSTEM_PACKAGE_NAME); 2190 } 2191 currentVersion = 119; 2192 } 2193 2194 // v120: Add double tap to wake setting. 2195 if (currentVersion == 119) { 2196 SettingsState secureSettings = getSecureSettingsLocked(userId); 2197 secureSettings.insertSettingLocked(Settings.Secure.DOUBLE_TAP_TO_WAKE, 2198 getContext().getResources().getBoolean( 2199 R.bool.def_double_tap_to_wake) ? "1" : "0", 2200 SettingsState.SYSTEM_PACKAGE_NAME); 2201 2202 currentVersion = 120; 2203 } 2204 2205 if (currentVersion == 120) { 2206 // Before 121, we used a different string encoding logic. We just bump the 2207 // version here; SettingsState knows how to handle pre-version 120 files. 2208 currentVersion = 121; 2209 } 2210 2211 if (currentVersion == 121) { 2212 // Version 122: allow OEMs to set a default payment component in resources. 2213 // Note that we only write the default if no default has been set; 2214 // if there is, we just leave the default at whatever it currently is. 2215 final SettingsState secureSettings = getSecureSettingsLocked(userId); 2216 String defaultComponent = (getContext().getResources().getString( 2217 R.string.def_nfc_payment_component)); 2218 Setting currentSetting = secureSettings.getSettingLocked( 2219 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT); 2220 if (defaultComponent != null && !defaultComponent.isEmpty() && 2221 currentSetting.isNull()) { 2222 secureSettings.insertSettingLocked( 2223 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 2224 defaultComponent, 2225 SettingsState.SYSTEM_PACKAGE_NAME); 2226 } 2227 currentVersion = 122; 2228 } 2229 2230 if (currentVersion == 122) { 2231 // Version 123: Adding a default value for the ability to add a user from 2232 // the lock screen. 2233 if (userId == UserHandle.USER_SYSTEM) { 2234 final SettingsState globalSettings = getGlobalSettingsLocked(); 2235 Setting currentSetting = globalSettings.getSettingLocked( 2236 Settings.Global.ADD_USERS_WHEN_LOCKED); 2237 if (currentSetting.isNull()) { 2238 globalSettings.insertSettingLocked( 2239 Settings.Global.ADD_USERS_WHEN_LOCKED, 2240 getContext().getResources().getBoolean( 2241 R.bool.def_add_users_from_lockscreen) ? "1" : "0", 2242 SettingsState.SYSTEM_PACKAGE_NAME); 2243 } 2244 } 2245 currentVersion = 123; 2246 } 2247 2248 if (currentVersion == 123) { 2249 final SettingsState globalSettings = getGlobalSettingsLocked(); 2250 String defaultDisabledProfiles = (getContext().getResources().getString( 2251 R.string.def_bluetooth_disabled_profiles)); 2252 globalSettings.insertSettingLocked(Settings.Global.BLUETOOTH_DISABLED_PROFILES, 2253 defaultDisabledProfiles, SettingsState.SYSTEM_PACKAGE_NAME); 2254 currentVersion = 124; 2255 } 2256 2257 if (currentVersion == 124) { 2258 // Version 124: allow OEMs to set a default value for whether IME should be 2259 // shown when a physical keyboard is connected. 2260 final SettingsState secureSettings = getSecureSettingsLocked(userId); 2261 Setting currentSetting = secureSettings.getSettingLocked( 2262 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); 2263 if (currentSetting.isNull()) { 2264 secureSettings.insertSettingLocked( 2265 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 2266 getContext().getResources().getBoolean( 2267 R.bool.def_show_ime_with_hard_keyboard) ? "1" : "0", 2268 SettingsState.SYSTEM_PACKAGE_NAME); 2269 } 2270 currentVersion = 125; 2271 } 2272 2273 if (currentVersion == 125) { 2274 // Version 125: Allow OEMs to set the default VR service. 2275 final SettingsState secureSettings = getSecureSettingsLocked(userId); 2276 2277 Setting currentSetting = secureSettings.getSettingLocked( 2278 Settings.Secure.ENABLED_VR_LISTENERS); 2279 if (currentSetting.isNull()) { 2280 ArraySet<ComponentName> l = 2281 SystemConfig.getInstance().getDefaultVrComponents(); 2282 2283 if (l != null && !l.isEmpty()) { 2284 StringBuilder b = new StringBuilder(); 2285 boolean start = true; 2286 for (ComponentName c : l) { 2287 if (!start) { 2288 b.append(':'); 2289 } 2290 b.append(c.flattenToString()); 2291 start = false; 2292 } 2293 secureSettings.insertSettingLocked( 2294 Settings.Secure.ENABLED_VR_LISTENERS, b.toString(), 2295 SettingsState.SYSTEM_PACKAGE_NAME); 2296 } 2297 2298 } 2299 currentVersion = 126; 2300 } 2301 2302 if (currentVersion == 126) { 2303 // Version 126: copy the primary values of LOCK_SCREEN_SHOW_NOTIFICATIONS and 2304 // LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS into managed profile. 2305 if (mUserManager.isManagedProfile(userId)) { 2306 final SettingsState systemSecureSettings = 2307 getSecureSettingsLocked(UserHandle.USER_SYSTEM); 2308 2309 final Setting showNotifications = systemSecureSettings.getSettingLocked( 2310 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS); 2311 if (!showNotifications.isNull()) { 2312 final SettingsState secureSettings = getSecureSettingsLocked(userId); 2313 secureSettings.insertSettingLocked( 2314 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 2315 showNotifications.getValue(), 2316 SettingsState.SYSTEM_PACKAGE_NAME); 2317 } 2318 2319 final Setting allowPrivate = systemSecureSettings.getSettingLocked( 2320 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); 2321 if (!allowPrivate.isNull()) { 2322 final SettingsState secureSettings = getSecureSettingsLocked(userId); 2323 secureSettings.insertSettingLocked( 2324 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 2325 allowPrivate.getValue(), 2326 SettingsState.SYSTEM_PACKAGE_NAME); 2327 } 2328 } 2329 currentVersion = 127; 2330 } 2331 2332 // vXXX: Add new settings above this point. 2333 2334 // Return the current version. 2335 return currentVersion; 2336 } 2337 } 2338 } 2339 } 2340