diff options
| author | 2009-07-02 05:01:23 -0700 | |
|---|---|---|
| committer | 2009-07-02 05:01:23 -0700 | |
| commit | 288fe16c20e2c20556839046d38c487b0b18735c (patch) | |
| tree | 277a19f204e5c95f0b5c36dbc33e8fca1ebab9f6 | |
| parent | eec11827a6c06b029030f43c8d54fd871cc3347d (diff) | |
| parent | 220f4d633be1098e7887dbd06f179138bf19f1ad (diff) | |
Merge change 6043 into donut
* changes:
  System and Secure settings backup.
5 files changed, 511 insertions, 2 deletions
| diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4a40058d5900..70173336af26 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1337,6 +1337,49 @@ public final class Settings {           */          public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions"; +        /** +         * Settings to backup. This is here so that it's in the same place as the settings +         * keys and easy to update. +         * @hide +         */ +        public static final String[] SETTINGS_TO_BACKUP = { +            STAY_ON_WHILE_PLUGGED_IN, +            END_BUTTON_BEHAVIOR, +            WIFI_SLEEP_POLICY, +            WIFI_USE_STATIC_IP, +            WIFI_STATIC_IP, +            WIFI_STATIC_GATEWAY, +            WIFI_STATIC_NETMASK, +            WIFI_STATIC_DNS1, +            WIFI_STATIC_DNS2, +            BLUETOOTH_DISCOVERABILITY, +            BLUETOOTH_DISCOVERABILITY_TIMEOUT, +            DIM_SCREEN, +            SCREEN_OFF_TIMEOUT, +            SCREEN_BRIGHTNESS, +            VIBRATE_ON, +            NOTIFICATIONS_USE_RING_VOLUME, +            RINGTONE, +            NOTIFICATION_SOUND, +            TEXT_AUTO_REPLACE, +            TEXT_AUTO_CAPS, +            TEXT_AUTO_PUNCTUATE, +            TEXT_SHOW_PASSWORD, +            AUTO_TIME, +            TIME_12_24, +            DATE_FORMAT, +            ACCELEROMETER_ROTATION, +            DTMF_TONE_WHEN_DIALING, +            DTMF_TONE_TYPE_WHEN_DIALING, +            EMERGENCY_TONE, +            CALL_AUTO_RETRY, +            HEARING_AID, +            TTY_MODE, +            SOUND_EFFECTS_ENABLED, +            HAPTIC_FEEDBACK_ENABLED, +            SHOW_WEB_SUGGESTIONS +        }; +          // Settings moved to Settings.Secure          /** @@ -2246,6 +2289,34 @@ public final class Settings {          public static final String LAST_SETUP_SHOWN = "last_setup_shown";          /** +         * @hide +         */ +        public static final String[] SETTINGS_TO_BACKUP = { +            INSTALL_NON_MARKET_APPS, +            PARENTAL_CONTROL_ENABLED, +            PARENTAL_CONTROL_REDIRECT_URL, +            USB_MASS_STORAGE_ENABLED, +            ACCESSIBILITY_ENABLED, +            ENABLED_ACCESSIBILITY_SERVICES, +            TTS_USE_DEFAULTS, +            TTS_DEFAULT_RATE, +            TTS_DEFAULT_PITCH, +            TTS_DEFAULT_SYNTH, +            TTS_DEFAULT_LANG, +            TTS_DEFAULT_COUNTRY, +            WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, +            WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, +            WIFI_NUM_ALLOWED_CHANNELS, +            WIFI_NUM_OPEN_NETWORKS_KEPT, +            BACKGROUND_DATA, +            PREFERRED_NETWORK_MODE, +            PREFERRED_TTY_MODE, +            CDMA_CELL_BROADCAST_SMS, +            PREFERRED_CDMA_SUBSCRIPTION, +            ENHANCED_VOICE_PRIVACY_ENABLED +        }; + +        /**           * Helper method for determining if a location provider is enabled.           * @param cr the content resolver to use           * @param provider the location provider to query diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index 2407d877a180..9109606baee9 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -4,6 +4,7 @@      <application android:allowClearUserData="false"                   android:label="@string/app_label" +                 android:backupAgent="SettingsBackupAgent"                   android:icon="@drawable/ic_launcher_settings">          <provider android:name="SettingsProvider" android:authorities="settings" diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java new file mode 100644 index 000000000000..1736a4918454 --- /dev/null +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.settings; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; + +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.backup.BackupHelperAgent; +import android.bluetooth.BluetoothDevice; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.media.AudioManager; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +/** + * Performs backup and restore of the System and Secure settings. + * List of settings that are backed up are stored in the Settings.java file + */ +public class SettingsBackupAgent extends BackupHelperAgent { + +    private static final String KEY_SYSTEM = "system"; +    private static final String KEY_SECURE = "secure"; +    private static final String KEY_SYNC = "sync_providers"; + +    private static String[] sortedSystemKeys = null; +    private static String[] sortedSecureKeys = null; + +    private static final byte[] EMPTY_DATA = new byte[0]; + +    private static final String TAG = "SettingsBackupAgent"; + +    private static final int COLUMN_ID = 0; +    private static final int COLUMN_NAME = 1; +    private static final int COLUMN_VALUE = 2; + +    private static final String[] PROJECTION = { +        Settings.NameValueTable._ID, +        Settings.NameValueTable.NAME, +        Settings.NameValueTable.VALUE +    }; + +    private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf"; +    private static final String FILE_BT_ROOT = "/data/misc/hcid/"; + +    private SettingsHelper mSettingsHelper; + +    public void onCreate() { +        mSettingsHelper = new SettingsHelper(this); +        super.onCreate(); +    } + +    @Override +    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, +            ParcelFileDescriptor newState) throws IOException { + +        byte[] systemSettingsData = getSystemSettings(); +        byte[] secureSettingsData = getSecureSettings(); +        byte[] syncProviders = mSettingsHelper.getSyncProviders(); +         +        data.writeEntityHeader(KEY_SYSTEM, systemSettingsData.length); +        data.writeEntityData(systemSettingsData, systemSettingsData.length); + +        data.writeEntityHeader(KEY_SECURE, secureSettingsData.length); +        data.writeEntityData(secureSettingsData, secureSettingsData.length); + +        data.writeEntityHeader(KEY_SYNC, syncProviders.length); +        data.writeEntityData(syncProviders, syncProviders.length); +         +        //TODO: Permissions problem : backupFile(FILE_WIFI_SUPPLICANT, data); +    } + +    @Override +    public void onRestore(BackupDataInput data, int appVersionCode, +            ParcelFileDescriptor newState) throws IOException { + +        enableWifi(false); +        enableBluetooth(false); + +        while (data.readNextHeader()) { +            final String key = data.getKey(); +            if (KEY_SYSTEM.equals(key)) { +                restoreSettings(data, Settings.System.CONTENT_URI); +            } else if (KEY_SECURE.equals(key)) { +                restoreSettings(data, Settings.Secure.CONTENT_URI); +            } else if (FILE_WIFI_SUPPLICANT.equals(key)) { +                restoreFile(FILE_WIFI_SUPPLICANT, data); +            } else if (KEY_SYNC.equals(key)) { +                mSettingsHelper.setSyncProviders(data); +            } else { +                data.skipEntityData(); +            } +        } +    } + +    private byte[] getSystemSettings() { +        Cursor sortedCursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, +                null, null, Settings.NameValueTable.NAME); +        // Copy and sort the array +        if (sortedSystemKeys == null) { +            sortedSystemKeys = copyAndSort(Settings.System.SETTINGS_TO_BACKUP); +        } +        byte[] result = extractRelevantValues(sortedCursor, sortedSystemKeys); +        sortedCursor.close(); +        return result; +    } + +    private byte[] getSecureSettings() { +        Cursor sortedCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, +                null, null, Settings.NameValueTable.NAME); +        // Copy and sort the array +        if (sortedSecureKeys == null) { +            sortedSecureKeys = copyAndSort(Settings.Secure.SETTINGS_TO_BACKUP); +        } +        byte[] result = extractRelevantValues(sortedCursor, sortedSecureKeys); +        sortedCursor.close(); +        return result; +    } + +    private void restoreSettings(BackupDataInput data, Uri contentUri) { +        ContentValues cv = new ContentValues(2); +        byte[] settings = new byte[data.getDataSize()]; +        try { +            data.readEntityData(settings, 0, settings.length); +        } catch (IOException ioe) { +            Log.e(TAG, "Couldn't read entity data"); +            return; +        } +        int pos = 0; +        while (pos < settings.length) { +            int length = readInt(settings, pos); +            pos += 4; +            String settingName = length > 0? new String(settings, pos, length) : null; +            pos += length; +            length = readInt(settings, pos); +            pos += 4; +            String settingValue = length > 0? new String(settings, pos, length) : null; +            pos += length; +            if (!TextUtils.isEmpty(settingName) && !TextUtils.isEmpty(settingValue)) { +                //Log.i(TAG, "Restore " + settingName + " = " + settingValue); +                cv.clear(); +                cv.put(Settings.NameValueTable.NAME, settingName); +                cv.put(Settings.NameValueTable.VALUE, settingValue); +                getContentResolver().insert(contentUri, cv); +                mSettingsHelper.restoreValue(settingName, settingValue); +            } +        } +    } + +    private String[] copyAndSort(String[] keys) { +        String[] sortedKeys = new String[keys.length]; +        System.arraycopy(keys, 0, sortedKeys, 0, keys.length); +        Arrays.sort(sortedKeys); +        return sortedKeys; +    } + +    /** +     * Given a cursor sorted by key name and a set of keys sorted by name,  +     * extract the required keys and values and write them to a byte array. +     * @param sortedCursor +     * @param sortedKeys +     * @return +     */ +    byte[] extractRelevantValues(Cursor sortedCursor, String[] sortedKeys) { +        byte[][] values = new byte[sortedKeys.length * 2][]; // keys and values +        if (!sortedCursor.moveToFirst()) { +            Log.e(TAG, "Couldn't read from the cursor"); +            return new byte[0]; +        } +        int keyIndex = 0; +        int totalSize = 0; +        while (!sortedCursor.isAfterLast()) { +            String name = sortedCursor.getString(COLUMN_NAME); +            while (sortedKeys[keyIndex].compareTo(name.toString()) < 0) { +                keyIndex++; +                if (keyIndex == sortedKeys.length) break; +            } +            if (keyIndex < sortedKeys.length && name.equals(sortedKeys[keyIndex])) { +                String value = sortedCursor.getString(COLUMN_VALUE); +                byte[] nameBytes = name.toString().getBytes(); +                totalSize += 4 + nameBytes.length; +                values[keyIndex * 2] = nameBytes; +                byte[] valueBytes; +                if (TextUtils.isEmpty(value)) { +                    valueBytes = null; +                    totalSize += 4; +                } else { +                    valueBytes = value.toString().getBytes(); +                    totalSize += 4 + valueBytes.length; +                    //Log.i(TAG, "Backing up " + name + " = " + value); +                } +                values[keyIndex * 2 + 1] = valueBytes; +                keyIndex++; +            } +            if (keyIndex == sortedKeys.length || !sortedCursor.moveToNext()) { +                break; +            } +        } + +        byte[] result = new byte[totalSize]; +        int pos = 0; +        for (int i = 0; i < sortedKeys.length * 2; i++) { +            if (values[i] != null) { +                pos = writeInt(result, pos, values[i].length); +                pos = writeBytes(result, pos, values[i]); +            } +        } +        return result; +    } + +    private void backupFile(String filename, BackupDataOutput data) { +        try { +            File file = new File(filename); +            if (file.exists()) { +                byte[] bytes = new byte[(int) file.length()]; +                FileInputStream fis = new FileInputStream(file); +                int offset = 0; +                int got = 0; +                do { +                    got = fis.read(bytes, offset, bytes.length - offset); +                    if (got > 0) offset += got; +                } while (offset < bytes.length && got > 0); +                data.writeEntityHeader(filename, bytes.length); +                data.writeEntityData(bytes, bytes.length); +            } else { +                data.writeEntityHeader(filename, 0); +                data.writeEntityData(EMPTY_DATA, 0); +            } +        } catch (IOException ioe) { +            Log.w(TAG, "Couldn't backup " + filename); +        } +    } + +    private void restoreFile(String filename, BackupDataInput data) { +        byte[] bytes = new byte[data.getDataSize()]; +        if (bytes.length <= 0) return; +        try { +            data.readEntityData(bytes, 0, bytes.length); +            FileOutputStream fos = new FileOutputStream(filename); +            fos.write(bytes); +        } catch (IOException ioe) { +            Log.w(TAG, "Couldn't restore " + filename); +        } +    } + +    /** +     * Write an int in BigEndian into the byte array. +     * @param out byte array +     * @param pos current pos in array +     * @param value integer to write +     * @return the index after adding the size of an int (4) +     */ +    private int writeInt(byte[] out, int pos, int value) { +        out[pos + 0] = (byte) ((value >> 24) & 0xFF); +        out[pos + 1] = (byte) ((value >> 16) & 0xFF); +        out[pos + 2] = (byte) ((value >>  8) & 0xFF); +        out[pos + 3] = (byte) ((value >>  0) & 0xFF); +        return pos + 4; +    } + +    private int writeBytes(byte[] out, int pos, byte[] value) { +        System.arraycopy(value, 0, out, pos, value.length); +        return pos + value.length; +    } + +    private int readInt(byte[] in, int pos) { +        int result = +                ((in[pos    ] & 0xFF) << 24) | +                ((in[pos + 1] & 0xFF) << 16) | +                ((in[pos + 2] & 0xFF) <<  8) | +                ((in[pos + 3] & 0xFF) <<  0); +        return result; +    } + +    private void enableWifi(boolean enable) { +        WifiManager wfm = (WifiManager) getSystemService(Context.WIFI_SERVICE); +        if (wfm != null) { +            wfm.setWifiEnabled(enable); +        } +    } + +    private void enableBluetooth(boolean enable) { +        BluetoothDevice bt = (BluetoothDevice) getSystemService(Context.BLUETOOTH_SERVICE); +        if (bt != null) { +            if (!enable) { +                bt.disable(); +            } else { +                bt.enable(); +            } +        } +    } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java new file mode 100644 index 000000000000..b0655872df29 --- /dev/null +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.settings; + +import android.backup.BackupDataInput; +import android.content.ContentResolver; +import android.content.Context; +import android.media.AudioManager; +import android.os.IHardwareService; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.content.IContentService; +import android.util.Log; + +public class SettingsHelper { +    private static final String TAG = "SettingsHelper"; + +    private Context mContext; +    private AudioManager mAudioManager; +    private IContentService mContentService; +    private static final String SYNC_AUTO = "auto_sync"; +    private static final String SYNC_MAIL = "gmail-ls_sync"; +    private static final String SYNC_CALENDAR = "calendar_sync"; +    private static final String SYNC_CONTACTS = "contacts_sync"; + +    public SettingsHelper(Context context) { +        mContext = context; +        mAudioManager = (AudioManager) context +                .getSystemService(Context.AUDIO_SERVICE); +        mContentService = ContentResolver.getContentService(); +    } + +    public void restoreValue(String name, String value) { +        if (Settings.System.SCREEN_BRIGHTNESS.equals(name)) { +            setBrightness(Integer.parseInt(value)); +        } else if (Settings.System.SOUND_EFFECTS_ENABLED.equals(name)) { +            if (Integer.parseInt(value) == 1) { +                mAudioManager.loadSoundEffects(); +            } else { +                mAudioManager.unloadSoundEffects(); +            } +        } +    } + +    private void setBrightness(int brightness) { +        try { +            IHardwareService hardware = IHardwareService.Stub +                    .asInterface(ServiceManager.getService("hardware")); +            if (hardware != null) { +                hardware.setBacklights(brightness); +            } +        } catch (RemoteException doe) { + +        } +    } + +    static final String[] PROVIDERS = { "gmail-ls", "calendar", "contacts" }; +     +    byte[] getSyncProviders() { +        byte[] sync = new byte[1 + PROVIDERS.length]; +        try { +            sync[0] = (byte) (mContentService.getListenForNetworkTickles() ? 1 : 0); +            for (int i = 0; i < PROVIDERS.length; i++) { +                sync[i + 1] = (byte)  +                        (mContentService.getSyncProviderAutomatically(PROVIDERS[i]) ? 1 : 0); +            } +        } catch (RemoteException re) { +            Log.w(TAG, "Unable to backup sync providers"); +            return sync; +        } +        return sync; +    } +     +    void setSyncProviders(BackupDataInput backup) { +        byte[] sync = new byte[backup.getDataSize()]; + +        try { +            backup.readEntityData(sync, 0, sync.length); +            mContentService.setListenForNetworkTickles(sync[0] == 1); +            for (int i = 0; i < PROVIDERS.length; i++) { +                mContentService.setSyncProviderAutomatically(PROVIDERS[i], sync[i + 1] > 0); +            } +        } catch (RemoteException re) { +            Log.w(TAG, "Unable to restore sync providers"); +        } catch (java.io.IOException ioe) { +            Log.w(TAG, "Unable to read sync settings"); +        } +    } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 3db52ebd00bf..a21bf320e7a1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -16,6 +16,9 @@  package com.android.providers.settings; +import java.io.FileNotFoundException; + +import android.backup.IBackupManager;  import android.content.ContentProvider;  import android.content.ContentUris;  import android.content.ContentValues; @@ -27,6 +30,7 @@ import android.database.sqlite.SQLiteQueryBuilder;  import android.media.RingtoneManager;  import android.net.Uri;  import android.os.ParcelFileDescriptor; +import android.os.ServiceManager;  import android.os.SystemProperties;  import android.provider.DrmStore;  import android.provider.MediaStore; @@ -34,8 +38,6 @@ import android.provider.Settings;  import android.text.TextUtils;  import android.util.Log; -import java.io.FileNotFoundException; -  public class SettingsProvider extends ContentProvider {      private static final String TAG = "SettingsProvider";      private static final boolean LOCAL_LOGV = false; @@ -137,6 +139,17 @@ public class SettingsProvider extends ContentProvider {              SystemProperties.set(property, Long.toString(version));          } +        // Inform the backup manager about a data change +        IBackupManager ibm = IBackupManager.Stub.asInterface( +                ServiceManager.getService(Context.BACKUP_SERVICE)); +        if (ibm != null) { +            try { +                ibm.dataChanged(getContext().getPackageName()); +            } catch (Exception e) { +                // Try again later +            } +        } +          // Now send the notification through the content framework.          String notify = uri.getQueryParameter("notify"); |