1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app.backup;
18
19import android.app.backup.RestoreObserver;
20import android.app.backup.RestoreSet;
21import android.app.backup.IRestoreObserver;
22import android.app.backup.IRestoreSession;
23import android.content.Context;
24import android.os.Handler;
25import android.os.Message;
26import android.os.RemoteException;
27import android.util.Log;
28
29/**
30 * Interface for managing a restore session.
31 * @hide
32 */
33public class RestoreSession {
34    static final String TAG = "RestoreSession";
35
36    final Context mContext;
37    IRestoreSession mBinder;
38    RestoreObserverWrapper mObserver = null;
39
40    /**
41     * Ask the current transport what the available restore sets are.
42     *
43     * @param observer a RestoreObserver object whose restoreSetsAvailable() method will
44     *   be called on the application's main thread in order to supply the results of
45     *   the restore set lookup by the backup transport.  This parameter must not be
46     *   null.
47     * @return Zero on success, nonzero on error.  The observer's restoreSetsAvailable()
48     *   method will only be called if this method returned zero.
49     */
50    public int getAvailableRestoreSets(RestoreObserver observer) {
51        int err = -1;
52        RestoreObserverWrapper obsWrapper = new RestoreObserverWrapper(mContext, observer);
53        try {
54            err = mBinder.getAvailableRestoreSets(obsWrapper);
55        } catch (RemoteException e) {
56            Log.d(TAG, "Can't contact server to get available sets");
57        }
58        return err;
59    }
60
61    /**
62     * Restore the given set onto the device, replacing the current data of any app
63     * contained in the restore set with the data previously backed up.
64     *
65     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
66     *
67     * @return Zero on success; nonzero on error.  The observer will only receive
68     *   progress callbacks if this method returned zero.
69     * @param token The token from {@link #getAvailableRestoreSets()} corresponding to
70     *   the restore set that should be used.
71     * @param observer If non-null, this binder points to an object that will receive
72     *   progress callbacks during the restore operation.
73     */
74    public int restoreAll(long token, RestoreObserver observer) {
75        int err = -1;
76        if (mObserver != null) {
77            Log.d(TAG, "restoreAll() called during active restore");
78            return -1;
79        }
80        mObserver = new RestoreObserverWrapper(mContext, observer);
81        try {
82            err = mBinder.restoreAll(token, mObserver);
83        } catch (RemoteException e) {
84            Log.d(TAG, "Can't contact server to restore");
85        }
86        return err;
87    }
88
89    /**
90     * Restore select packages from the given set onto the device, replacing the
91     * current data of any app contained in the set with the data previously
92     * backed up.
93     *
94     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
95     *
96     * @return Zero on success, nonzero on error. The observer will only receive
97     *   progress callbacks if this method returned zero.
98     * @param token The token from {@link getAvailableRestoreSets()} corresponding to
99     *   the restore set that should be used.
100     * @param observer If non-null, this binder points to an object that will receive
101     *   progress callbacks during the restore operation.
102     * @param packages The set of packages for which to attempt a restore.  Regardless of
103     *   the contents of the actual back-end dataset named by {@code token}, only
104     *   applications mentioned in this list will have their data restored.
105     *
106     * @hide
107     */
108    public int restoreSome(long token, RestoreObserver observer, String[] packages) {
109        int err = -1;
110        if (mObserver != null) {
111            Log.d(TAG, "restoreAll() called during active restore");
112            return -1;
113        }
114        mObserver = new RestoreObserverWrapper(mContext, observer);
115        try {
116            err = mBinder.restoreSome(token, mObserver, packages);
117        } catch (RemoteException e) {
118            Log.d(TAG, "Can't contact server to restore packages");
119        }
120        return err;
121    }
122
123    /**
124     * Restore a single application from backup.  The data will be restored from the
125     * current backup dataset if the given package has stored data there, or from
126     * the dataset used during the last full device setup operation if the current
127     * backup dataset has no matching data.  If no backup data exists for this package
128     * in either source, a nonzero value will be returned.
129     *
130     * @return Zero on success; nonzero on error.  The observer will only receive
131     *   progress callbacks if this method returned zero.
132     * @param packageName The name of the package whose data to restore.  If this is
133     *   not the name of the caller's own package, then the android.permission.BACKUP
134     *   permission must be held.
135     * @param observer If non-null, this binder points to an object that will receive
136     *   progress callbacks during the restore operation.
137     */
138    public int restorePackage(String packageName, RestoreObserver observer) {
139        int err = -1;
140        if (mObserver != null) {
141            Log.d(TAG, "restorePackage() called during active restore");
142            return -1;
143        }
144        mObserver = new RestoreObserverWrapper(mContext, observer);
145        try {
146            err = mBinder.restorePackage(packageName, mObserver);
147        } catch (RemoteException e) {
148            Log.d(TAG, "Can't contact server to restore package");
149        }
150        return err;
151    }
152
153    /**
154     * End this restore session.  After this method is called, the RestoreSession
155     * object is no longer valid.
156     *
157     * <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
158     *   even if {@link #restorePackage(String, RestoreObserver)} failed.
159     */
160    public void endRestoreSession() {
161        try {
162            mBinder.endRestoreSession();
163        } catch (RemoteException e) {
164            Log.d(TAG, "Can't contact server to get available sets");
165        } finally {
166            mBinder = null;
167        }
168    }
169
170    /*
171     * Nonpublic implementation here
172     */
173
174    RestoreSession(Context context, IRestoreSession binder) {
175        mContext = context;
176        mBinder = binder;
177    }
178
179    /*
180     * We wrap incoming binder calls with a private class implementation that
181     * redirects them into main-thread actions.  This serializes the restore
182     * progress callbacks nicely within the usual main-thread lifecycle pattern.
183     */
184    private class RestoreObserverWrapper extends IRestoreObserver.Stub {
185        final Handler mHandler;
186        final RestoreObserver mAppObserver;
187
188        static final int MSG_RESTORE_STARTING = 1;
189        static final int MSG_UPDATE = 2;
190        static final int MSG_RESTORE_FINISHED = 3;
191        static final int MSG_RESTORE_SETS_AVAILABLE = 4;
192
193        RestoreObserverWrapper(Context context, RestoreObserver appObserver) {
194            mHandler = new Handler(context.getMainLooper()) {
195                @Override
196                public void handleMessage(Message msg) {
197                    switch (msg.what) {
198                    case MSG_RESTORE_STARTING:
199                        mAppObserver.restoreStarting(msg.arg1);
200                        break;
201                    case MSG_UPDATE:
202                        mAppObserver.onUpdate(msg.arg1, (String)msg.obj);
203                        break;
204                    case MSG_RESTORE_FINISHED:
205                        mAppObserver.restoreFinished(msg.arg1);
206                        break;
207                    case MSG_RESTORE_SETS_AVAILABLE:
208                        mAppObserver.restoreSetsAvailable((RestoreSet[])msg.obj);
209                        break;
210                    }
211                }
212            };
213            mAppObserver = appObserver;
214        }
215
216        // Binder calls into this object just enqueue on the main-thread handler
217        public void restoreSetsAvailable(RestoreSet[] result) {
218            mHandler.sendMessage(
219                    mHandler.obtainMessage(MSG_RESTORE_SETS_AVAILABLE, result));
220        }
221
222        public void restoreStarting(int numPackages) {
223            mHandler.sendMessage(
224                    mHandler.obtainMessage(MSG_RESTORE_STARTING, numPackages, 0));
225        }
226
227        public void onUpdate(int nowBeingRestored, String currentPackage) {
228            mHandler.sendMessage(
229                    mHandler.obtainMessage(MSG_UPDATE, nowBeingRestored, 0, currentPackage));
230        }
231
232        public void restoreFinished(int error) {
233            mHandler.sendMessage(
234                    mHandler.obtainMessage(MSG_RESTORE_FINISHED, error, 0));
235        }
236    }
237}
238