2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.providers.settings;
19 import android.app.ActivityManager;
20 import android.app.IActivityManager;
21 import android.app.backup.IBackupManager;
22 import android.content.ContentResolver;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.res.Configuration;
27 import android.location.LocationManager;
28 import android.media.AudioManager;
29 import android.media.RingtoneManager;
30 import android.net.Uri;
31 import android.os.IPowerManager;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.provider.Settings;
37 import android.telephony.TelephonyManager;
38 import android.text.TextUtils;
39 import android.util.ArraySet;
40 import android.util.Slog;
42 import java.util.Locale;
44 public class SettingsHelper {
45 private static final String TAG = "SettingsHelper";
46 private static final String SILENT_RINGTONE = "_silent";
47 private Context mContext;
48 private AudioManager mAudioManager;
49 private TelephonyManager mTelephonyManager;
52 * A few settings elements are special in that a restore of those values needs to
53 * be post-processed by relevant parts of the OS. A restore of any settings element
54 * mentioned in this table will therefore cause the system to send a broadcast with
55 * the {@link Intent#ACTION_SETTING_RESTORED} action, with extras naming the
56 * affected setting and supplying its pre-restore value for comparison.
58 * @see Intent#ACTION_SETTING_RESTORED
59 * @see System#SETTINGS_TO_BACKUP
60 * @see Secure#SETTINGS_TO_BACKUP
61 * @see Global#SETTINGS_TO_BACKUP
65 private static final ArraySet<String> sBroadcastOnRestore;
67 sBroadcastOnRestore = new ArraySet<String>(5);
68 sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
69 sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
70 sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
71 sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
72 sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
75 private interface SettingsLookup {
76 public String lookup(ContentResolver resolver, String name, int userHandle);
79 private static SettingsLookup sSystemLookup = new SettingsLookup() {
80 public String lookup(ContentResolver resolver, String name, int userHandle) {
81 return Settings.System.getStringForUser(resolver, name, userHandle);
85 private static SettingsLookup sSecureLookup = new SettingsLookup() {
86 public String lookup(ContentResolver resolver, String name, int userHandle) {
87 return Settings.Secure.getStringForUser(resolver, name, userHandle);
91 private static SettingsLookup sGlobalLookup = new SettingsLookup() {
92 public String lookup(ContentResolver resolver, String name, int userHandle) {
93 return Settings.Global.getStringForUser(resolver, name, userHandle);
97 public SettingsHelper(Context context) {
99 mAudioManager = (AudioManager) context
100 .getSystemService(Context.AUDIO_SERVICE);
101 mTelephonyManager = (TelephonyManager) context
102 .getSystemService(Context.TELEPHONY_SERVICE);
106 * Sets the property via a call to the appropriate API, if any, and returns
107 * whether or not the setting should be saved to the database as well.
108 * @param name the name of the setting
109 * @param value the string value of the setting
110 * @return whether to continue with writing the value to the database. In
111 * some cases the data will be written by the call to the appropriate API,
112 * and in some cases the property value needs to be modified before setting.
114 public void restoreValue(Context context, ContentResolver cr, ContentValues contentValues,
115 Uri destination, String name, String value, int restoredFromSdkInt) {
116 // Will we need a post-restore broadcast for this element?
117 String oldValue = null;
118 boolean sendBroadcast = false;
119 final SettingsLookup table;
121 if (destination.equals(Settings.Secure.CONTENT_URI)) {
122 table = sSecureLookup;
123 } else if (destination.equals(Settings.System.CONTENT_URI)) {
124 table = sSystemLookup;
125 } else { /* must be GLOBAL; this was preflighted by the caller */
126 table = sGlobalLookup;
129 if (sBroadcastOnRestore.contains(name)) {
130 // TODO: http://b/22388012
131 oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
132 sendBroadcast = true;
136 if (Settings.System.SCREEN_BRIGHTNESS.equals(name)) {
137 setBrightness(Integer.parseInt(value));
138 // fall through to the ordinary write to settings
139 } else if (Settings.System.SOUND_EFFECTS_ENABLED.equals(name)) {
140 setSoundEffects(Integer.parseInt(value) == 1);
141 // fall through to the ordinary write to settings
142 } else if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
143 setGpsLocation(value);
145 } else if (Settings.Secure.BACKUP_AUTO_RESTORE.equals(name)) {
146 setAutoRestore(Integer.parseInt(value) == 1);
147 } else if (isAlreadyConfiguredCriticalAccessibilitySetting(name)) {
149 } else if (Settings.System.RINGTONE.equals(name)
150 || Settings.System.NOTIFICATION_SOUND.equals(name)) {
151 setRingtone(name, value);
155 // Default case: write the restored value to settings
156 contentValues.clear();
157 contentValues.put(Settings.NameValueTable.NAME, name);
158 contentValues.put(Settings.NameValueTable.VALUE, value);
159 cr.insert(destination, contentValues);
160 } catch (Exception e) {
161 // If we fail to apply the setting, by definition nothing happened
162 sendBroadcast = false;
164 // If this was an element of interest, send the "we just restored it"
165 // broadcast with the historical value now that the new value has
166 // been committed and observers kicked off.
168 Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
169 .setPackage("android").addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
170 .putExtra(Intent.EXTRA_SETTING_NAME, name)
171 .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
172 .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue)
173 .putExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, restoredFromSdkInt);
174 context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
179 public String onBackupValue(String name, String value) {
180 // Special processing for backing up ringtones & notification sounds
181 if (Settings.System.RINGTONE.equals(name)
182 || Settings.System.NOTIFICATION_SOUND.equals(name)) {
184 if (Settings.System.RINGTONE.equals(name)) {
185 // For ringtones, we need to distinguish between non-telephony vs telephony
186 if (mTelephonyManager != null && mTelephonyManager.isVoiceCapable()) {
187 // Backup a null ringtone as silent on voice-capable devices
188 return SILENT_RINGTONE;
190 // Skip backup of ringtone on non-telephony devices.
194 // Backup a null notification sound as silent
195 return SILENT_RINGTONE;
198 return getCanonicalRingtoneValue(value);
201 // Return the original value
206 * Sets the ringtone of type specified by the name.
208 * @param name should be Settings.System.RINGTONE or Settings.System.NOTIFICATION_SOUND.
209 * @param value can be a canonicalized uri or "_silent" to indicate a silent (null) ringtone.
211 private void setRingtone(String name, String value) {
212 // If it's null, don't change the default
213 if (value == null) return;
214 Uri ringtoneUri = null;
215 if (SILENT_RINGTONE.equals(value)) {
218 Uri canonicalUri = Uri.parse(value);
219 ringtoneUri = mContext.getContentResolver().uncanonicalize(canonicalUri);
220 if (ringtoneUri == null) {
221 // Unrecognized or invalid Uri, don't restore
225 final int ringtoneType = Settings.System.RINGTONE.equals(name)
226 ? RingtoneManager.TYPE_RINGTONE : RingtoneManager.TYPE_NOTIFICATION;
227 RingtoneManager.setActualDefaultRingtoneUri(mContext, ringtoneType, ringtoneUri);
230 private String getCanonicalRingtoneValue(String value) {
231 final Uri ringtoneUri = Uri.parse(value);
232 final Uri canonicalUri = mContext.getContentResolver().canonicalize(ringtoneUri);
233 return canonicalUri == null ? null : canonicalUri.toString();
236 private boolean isAlreadyConfiguredCriticalAccessibilitySetting(String name) {
237 // These are the critical accessibility settings that are required for users with
238 // accessibility needs to be able to interact with the device. If these settings are
239 // already configured, we will not overwrite them. If they are already set,
240 // it means that the user has performed a global gesture to enable accessibility or set
241 // these settings in the Accessibility portion of the Setup Wizard, and definitely needs
242 // these features working after the restore.
244 case Settings.Secure.ACCESSIBILITY_ENABLED:
245 case Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD:
246 case Settings.Secure.TOUCH_EXPLORATION_ENABLED:
247 case Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
248 case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
249 case Settings.Secure.UI_NIGHT_MODE:
250 return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
251 case Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES:
252 case Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES:
253 case Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
254 case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE:
255 return !TextUtils.isEmpty(Settings.Secure.getString(
256 mContext.getContentResolver(), name));
257 case Settings.System.FONT_SCALE:
258 return Settings.System.getFloat(mContext.getContentResolver(), name, 1.0f) != 1.0f;
264 private void setAutoRestore(boolean enabled) {
266 IBackupManager bm = IBackupManager.Stub.asInterface(
267 ServiceManager.getService(Context.BACKUP_SERVICE));
269 bm.setAutoRestore(enabled);
271 } catch (RemoteException e) {}
274 private void setGpsLocation(String value) {
275 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
276 if (um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)) {
279 final String GPS = LocationManager.GPS_PROVIDER;
282 value.startsWith(GPS + ",") ||
283 value.endsWith("," + GPS) ||
284 value.contains("," + GPS + ",");
285 Settings.Secure.setLocationProviderEnabled(
286 mContext.getContentResolver(), GPS, enabled);
289 private void setSoundEffects(boolean enable) {
291 mAudioManager.loadSoundEffects();
293 mAudioManager.unloadSoundEffects();
297 private void setBrightness(int brightness) {
299 IPowerManager power = IPowerManager.Stub.asInterface(
300 ServiceManager.getService("power"));
302 power.setTemporaryScreenBrightnessSettingOverride(brightness);
304 } catch (RemoteException doe) {
309 byte[] getLocaleData() {
310 Configuration conf = mContext.getResources().getConfiguration();
311 final Locale loc = conf.locale;
312 String localeString = loc.getLanguage();
313 String country = loc.getCountry();
314 if (!TextUtils.isEmpty(country)) {
315 localeString += "-" + country;
317 return localeString.getBytes();
321 * Sets the locale specified. Input data is the byte representation of a
322 * BCP-47 language tag. For backwards compatibility, strings of the form
323 * {@code ll_CC} are also accepted, where {@code ll} is a two letter language
324 * code and {@code CC} is a two letter country code.
326 * @param data the locale string in bytes.
328 void setLocaleData(byte[] data, int size) {
329 // Check if locale was set by the user:
330 final ContentResolver cr = mContext.getContentResolver();
331 final boolean userSetLocale = mContext.getResources().getConfiguration().userSetLocale;
332 final boolean provisioned = Settings.Global.getInt(cr,
333 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
334 if (userSetLocale || provisioned) {
335 // Don't change if user set it in the SetupWizard, or if this is a post-setup
336 // deferred restore operation
337 Slog.i(TAG, "Not applying restored locale; "
338 + (userSetLocale ? "user already specified" : "device already provisioned"));
342 final String[] availableLocales = mContext.getAssets().getLocales();
343 // Replace "_" with "-" to deal with older backups.
344 String localeCode = new String(data, 0, size).replace('_', '-');
346 for (int i = 0; i < availableLocales.length; i++) {
347 if (availableLocales[i].equals(localeCode)) {
348 loc = Locale.forLanguageTag(localeCode);
352 if (loc == null) return; // Couldn't find the saved locale in this version of the software
355 IActivityManager am = ActivityManager.getService();
356 Configuration config = am.getConfiguration();
358 // indicate this isn't some passing default - the user wants this remembered
359 config.userSetLocale = true;
361 am.updateConfiguration(config);
362 } catch (RemoteException e) {
363 // Intentionally left blank
368 * Informs the audio service of changes to the settings so that
369 * they can be re-read and applied.
371 void applyAudioSettings() {
372 AudioManager am = new AudioManager(mContext);
373 am.reloadAudioSettings();