1b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato/*
2b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * Copyright (C) 2009 The Android Open Source Project
3b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato *
4b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * Licensed under the Apache License, Version 2.0 (the "License");
5b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * you may not use this file except in compliance with the License.
6b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * You may obtain a copy of the License at
7b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato *
8b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato *      http://www.apache.org/licenses/LICENSE-2.0
9b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato *
10b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * Unless required by applicable law or agreed to in writing, software
11b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * distributed under the License is distributed on an "AS IS" BASIS,
12b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * See the License for the specific language governing permissions and
14b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato * limitations under the License.
15b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato */
16b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
174528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tatepackage android.app.backup;
18b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
196f9d58ac62366b13a1eac00d58ebc84f03cea3f2Brad Fitzpatrickimport android.app.QueuedWork;
20b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport android.content.Context;
21b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport android.os.ParcelFileDescriptor;
2206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onoratoimport android.util.Log;
23b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
2406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onoratoimport java.io.File;
25b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
26e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate/**
27d17da43c82c4edb97514d6138bc208eeba321636Scott Main * A helper class that can be used in conjunction with
28cc84c69726507a85116f5664e20e2ebfac76edbeChristopher Tate * {@link android.app.backup.BackupAgentHelper} to manage the backup of
29d17da43c82c4edb97514d6138bc208eeba321636Scott Main * {@link android.content.SharedPreferences}. Whenever a backup is performed, it
30d17da43c82c4edb97514d6138bc208eeba321636Scott Main * will back up all named shared preferences that have changed since the last
314e14a829129feee14ebe453f61a124784c870610Christopher Tate * backup operation.
325a20ea16d7e5b70dc7bad8700f54170e4f220d12Kenny Root * <p>
33d17da43c82c4edb97514d6138bc208eeba321636Scott Main * To use this class, the application's backup agent class should extend
344e14a829129feee14ebe453f61a124784c870610Christopher Tate * {@link android.app.backup.BackupAgentHelper}.  Then, in the agent's
354e14a829129feee14ebe453f61a124784c870610Christopher Tate * {@link BackupAgent#onCreate()} method, an instance of this class should be
364e14a829129feee14ebe453f61a124784c870610Christopher Tate * allocated and installed as a backup/restore handler within the BackupAgentHelper
37d17da43c82c4edb97514d6138bc208eeba321636Scott Main * framework.  For example, an agent supporting backup and restore for
38d17da43c82c4edb97514d6138bc208eeba321636Scott Main * an application with two groups of {@link android.content.SharedPreferences}
394e14a829129feee14ebe453f61a124784c870610Christopher Tate * data might look something like this:
404e14a829129feee14ebe453f61a124784c870610Christopher Tate * <pre>
414e14a829129feee14ebe453f61a124784c870610Christopher Tate * import android.app.backup.BackupAgentHelper;
424e14a829129feee14ebe453f61a124784c870610Christopher Tate * import android.app.backup.SharedPreferencesBackupHelper;
434e14a829129feee14ebe453f61a124784c870610Christopher Tate *
444e14a829129feee14ebe453f61a124784c870610Christopher Tate * public class MyBackupAgent extends BackupAgentHelper {
454e14a829129feee14ebe453f61a124784c870610Christopher Tate *     // The names of the SharedPreferences groups that the application maintains.  These
464e14a829129feee14ebe453f61a124784c870610Christopher Tate *     // are the same strings that are passed to {@link Context#getSharedPreferences(String, int)}.
474e14a829129feee14ebe453f61a124784c870610Christopher Tate *     static final String PREFS_DISPLAY = "displayprefs";
484e14a829129feee14ebe453f61a124784c870610Christopher Tate *     static final String PREFS_SCORES = "highscores";
494e14a829129feee14ebe453f61a124784c870610Christopher Tate *
504e14a829129feee14ebe453f61a124784c870610Christopher Tate *     // An arbitrary string used within the BackupAgentHelper implementation to
514e14a829129feee14ebe453f61a124784c870610Christopher Tate *     // identify the SharedPreferenceBackupHelper's data.
524e14a829129feee14ebe453f61a124784c870610Christopher Tate *     static final String MY_PREFS_BACKUP_KEY = "myprefs";
534e14a829129feee14ebe453f61a124784c870610Christopher Tate *
544e14a829129feee14ebe453f61a124784c870610Christopher Tate *     // Simply allocate a helper and install it
554e14a829129feee14ebe453f61a124784c870610Christopher Tate *     void onCreate() {
564e14a829129feee14ebe453f61a124784c870610Christopher Tate *         SharedPreferencesBackupHelper helper =
574e14a829129feee14ebe453f61a124784c870610Christopher Tate *                 new SharedPreferencesBackupHelper(this, PREFS_DISPLAY, PREFS_SCORES);
584e14a829129feee14ebe453f61a124784c870610Christopher Tate *         addHelper(MY_PREFS_BACKUP_KEY, helper);
594e14a829129feee14ebe453f61a124784c870610Christopher Tate *     }
604e14a829129feee14ebe453f61a124784c870610Christopher Tate * }</pre>
614e14a829129feee14ebe453f61a124784c870610Christopher Tate * <p>
62d17da43c82c4edb97514d6138bc208eeba321636Scott Main * No further implementation is needed; the {@link BackupAgentHelper} mechanism automatically
634e14a829129feee14ebe453f61a124784c870610Christopher Tate * dispatches the
644e14a829129feee14ebe453f61a124784c870610Christopher Tate * {@link BackupAgent#onBackup(android.os.ParcelFileDescriptor, BackupDataOutput, android.os.ParcelFileDescriptor) BackupAgent.onBackup()}
654e14a829129feee14ebe453f61a124784c870610Christopher Tate * and
664e14a829129feee14ebe453f61a124784c870610Christopher Tate * {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) BackupAgent.onRestore()}
67d17da43c82c4edb97514d6138bc208eeba321636Scott Main * callbacks to the SharedPreferencesBackupHelper as appropriate.
68e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate */
6906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onoratopublic class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper {
7006290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    private static final String TAG = "SharedPreferencesBackupHelper";
71436344ae12c819f58306ceb94241a266141e1218Christopher Tate    private static final boolean DEBUG = false;
7206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
731cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato    private Context mContext;
7406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    private String[] mPrefGroups;
751cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato
76e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate    /**
77e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     * Construct a helper for backing up and restoring the
78e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     * {@link android.content.SharedPreferences} under the given names.
79e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     *
80d17da43c82c4edb97514d6138bc208eeba321636Scott Main     * @param context The application {@link android.content.Context}
81d17da43c82c4edb97514d6138bc208eeba321636Scott Main     * @param prefGroups The names of each {@link android.content.SharedPreferences} file to
82d17da43c82c4edb97514d6138bc208eeba321636Scott Main     * back up
83e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     */
84dc355a90a3d9d34f66316928a53f61ac35ab4781Joe Onorato    public SharedPreferencesBackupHelper(Context context, String... prefGroups) {
8506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        super(context);
861cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato
871cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato        mContext = context;
8806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        mPrefGroups = prefGroups;
891cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato    }
9006290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
91e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate    /**
92d17da43c82c4edb97514d6138bc208eeba321636Scott Main     * Backs up the configured {@link android.content.SharedPreferences} groups.
93e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     */
9406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
9506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato            ParcelFileDescriptor newState) {
961cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato        Context context = mContext;
976f9d58ac62366b13a1eac00d58ebc84f03cea3f2Brad Fitzpatrick
986f9d58ac62366b13a1eac00d58ebc84f03cea3f2Brad Fitzpatrick        // If a SharedPreference has an outstanding write in flight,
996f9d58ac62366b13a1eac00d58ebc84f03cea3f2Brad Fitzpatrick        // wait for it to finish flushing to disk.
1006f9d58ac62366b13a1eac00d58ebc84f03cea3f2Brad Fitzpatrick        QueuedWork.waitToFinish();
1016f9d58ac62366b13a1eac00d58ebc84f03cea3f2Brad Fitzpatrick
102b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        // make filenames for the prefGroups
10306290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        String[] prefGroups = mPrefGroups;
104b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        final int N = prefGroups.length;
105b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        String[] files = new String[N];
106b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        for (int i=0; i<N; i++) {
1071cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato            files[i] = context.getSharedPrefsFile(prefGroups[i]).getAbsolutePath();
108b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        }
109b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
1101cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato        // go
11106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        performBackup_checked(oldState, data, newState, files, prefGroups);
11206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    }
11306290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
114e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate    /**
115e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     * Restores one entity from the restore data stream to its proper shared
116e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     * preferences file store.
117e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate     */
11806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public void restoreEntity(BackupDataInputStream data) {
11906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        Context context = mContext;
12006290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
12106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        String key = data.getKey();
122436344ae12c819f58306ceb94241a266141e1218Christopher Tate        if (DEBUG) Log.d(TAG, "got entity '" + key + "' size=" + data.size());
123436344ae12c819f58306ceb94241a266141e1218Christopher Tate
12406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        if (isKeyInList(key, mPrefGroups)) {
12506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato            File f = context.getSharedPrefsFile(key).getAbsoluteFile();
12606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato            writeFile(f, data);
12706290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        }
128b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    }
129b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato}
130