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