Bmgr.java revision 8f98252afea3fd0e68693635ec21b6004a52fa69
1/*
2 * Copyright (C) 2009 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 com.android.commands.bmgr;
18
19import android.app.backup.RestoreSet;
20import android.app.backup.IBackupManager;
21import android.app.backup.IRestoreObserver;
22import android.app.backup.IRestoreSession;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25
26import java.util.HashSet;
27
28public final class Bmgr {
29    IBackupManager mBmgr;
30    IRestoreSession mRestore;
31
32    static final String BMGR_NOT_RUNNING_ERR =
33            "Error: Could not access the Backup Manager.  Is the system running?";
34    static final String TRANSPORT_NOT_RUNNING_ERR =
35        "Error: Could not access the backup transport.  Is the system running?";
36
37    private String[] mArgs;
38    private int mNextArg;
39
40    public static void main(String[] args) {
41        try {
42            new Bmgr().run(args);
43        } catch (Exception e) {
44            System.err.println("Exception caught:");
45            e.printStackTrace();
46        }
47    }
48
49    public void run(String[] args) {
50        if (args.length < 1) {
51            showUsage();
52            return;
53        }
54
55        mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
56        if (mBmgr == null) {
57            System.err.println(BMGR_NOT_RUNNING_ERR);
58            return;
59        }
60
61        mArgs = args;
62        String op = args[0];
63        mNextArg = 1;
64
65        if ("enabled".equals(op)) {
66            doEnabled();
67            return;
68        }
69
70        if ("enable".equals(op)) {
71            doEnable();
72            return;
73        }
74
75        if ("run".equals(op)) {
76            doRun();
77            return;
78        }
79
80        if ("backup".equals(op)) {
81            doBackup();
82            return;
83        }
84
85        if ("list".equals(op)) {
86            doList();
87            return;
88        }
89
90        if ("restore".equals(op)) {
91            doRestore();
92            return;
93        }
94
95        if ("transport".equals(op)) {
96            doTransport();
97            return;
98        }
99
100        if ("wipe".equals(op)) {
101            doWipe();
102            return;
103        }
104
105        System.err.println("Unknown command");
106        showUsage();
107    }
108
109    private String enableToString(boolean enabled) {
110        return enabled ? "enabled" : "disabled";
111    }
112
113    private void doEnabled() {
114        try {
115            boolean isEnabled = mBmgr.isBackupEnabled();
116            System.out.println("Backup Manager currently "
117                    + enableToString(isEnabled));
118        } catch (RemoteException e) {
119            System.err.println(e.toString());
120            System.err.println(BMGR_NOT_RUNNING_ERR);
121        }
122    }
123
124    private void doEnable() {
125        String arg = nextArg();
126        if (arg == null) {
127            showUsage();
128            return;
129        }
130
131        try {
132            boolean enable = Boolean.parseBoolean(arg);
133            mBmgr.setBackupEnabled(enable);
134            System.out.println("Backup Manager now " + enableToString(enable));
135        } catch (NumberFormatException e) {
136            showUsage();
137            return;
138        } catch (RemoteException e) {
139            System.err.println(e.toString());
140            System.err.println(BMGR_NOT_RUNNING_ERR);
141        }
142    }
143
144    private void doRun() {
145        try {
146            mBmgr.backupNow();
147        } catch (RemoteException e) {
148            System.err.println(e.toString());
149            System.err.println(BMGR_NOT_RUNNING_ERR);
150        }
151    }
152
153    private void doBackup() {
154        String pkg = nextArg();
155        if (pkg == null) {
156            showUsage();
157            return;
158        }
159
160        try {
161            mBmgr.dataChanged(pkg);
162        } catch (RemoteException e) {
163            System.err.println(e.toString());
164            System.err.println(BMGR_NOT_RUNNING_ERR);
165        }
166    }
167
168    private void doTransport() {
169        try {
170            String which = nextArg();
171            if (which == null) {
172                showUsage();
173                return;
174            }
175
176            String old = mBmgr.selectBackupTransport(which);
177            if (old == null) {
178                System.out.println("Unknown transport '" + which
179                        + "' specified; no changes made.");
180            } else {
181                System.out.println("Selected transport " + which + " (formerly " + old + ")");
182            }
183        } catch (RemoteException e) {
184            System.err.println(e.toString());
185            System.err.println(BMGR_NOT_RUNNING_ERR);
186        }
187    }
188
189    private void doWipe() {
190        String transport = nextArg();
191        if (transport == null) {
192            showUsage();
193            return;
194        }
195
196        String pkg = nextArg();
197        if (pkg == null) {
198            showUsage();
199            return;
200        }
201
202        try {
203            mBmgr.clearBackupData(transport, pkg);
204            System.out.println("Wiped backup data for " + pkg + " on " + transport);
205        } catch (RemoteException e) {
206            System.err.println(e.toString());
207            System.err.println(BMGR_NOT_RUNNING_ERR);
208        }
209    }
210
211    private void doList() {
212        String arg = nextArg();     // sets, transports, packages set#
213        if ("transports".equals(arg)) {
214            doListTransports();
215            return;
216        }
217
218        // The rest of the 'list' options work with a restore session on the current transport
219        try {
220            mRestore = mBmgr.beginRestoreSession(null, null);
221            if (mRestore == null) {
222                System.err.println(BMGR_NOT_RUNNING_ERR);
223                return;
224            }
225
226            if ("sets".equals(arg)) {
227                doListRestoreSets();
228            } else if ("transports".equals(arg)) {
229                doListTransports();
230            }
231
232            mRestore.endRestoreSession();
233        } catch (RemoteException e) {
234            System.err.println(e.toString());
235            System.err.println(BMGR_NOT_RUNNING_ERR);
236        }
237    }
238
239    private void doListTransports() {
240        try {
241            String current = mBmgr.getCurrentTransport();
242            String[] transports = mBmgr.listAllTransports();
243            if (transports == null || transports.length == 0) {
244                System.out.println("No transports available.");
245                return;
246            }
247
248            for (String t : transports) {
249                String pad = (t.equals(current)) ? "  * " : "    ";
250                System.out.println(pad + t);
251            }
252        } catch (RemoteException e) {
253            System.err.println(e.toString());
254            System.err.println(BMGR_NOT_RUNNING_ERR);
255        }
256    }
257
258    private void doListRestoreSets() {
259        try {
260            RestoreObserver observer = new RestoreObserver();
261            int err = mRestore.getAvailableRestoreSets(observer);
262            if (err != 0) {
263                System.out.println("Unable to request restore sets");
264            } else {
265                observer.waitForCompletion();
266                printRestoreSets(observer.sets);
267            }
268        } catch (RemoteException e) {
269            System.err.println(e.toString());
270            System.err.println(TRANSPORT_NOT_RUNNING_ERR);
271        }
272    }
273
274    private void printRestoreSets(RestoreSet[] sets) {
275        if (sets == null || sets.length == 0) {
276            System.out.println("No restore sets");
277            return;
278        }
279        for (RestoreSet s : sets) {
280            System.out.println("  " + Long.toHexString(s.token) + " : " + s.name);
281        }
282    }
283
284    class RestoreObserver extends IRestoreObserver.Stub {
285        boolean done;
286        RestoreSet[] sets = null;
287
288        public void restoreSetsAvailable(RestoreSet[] result) {
289            synchronized (this) {
290                sets = result;
291                done = true;
292                this.notify();
293            }
294        }
295
296        public void restoreStarting(int numPackages) {
297            System.out.println("restoreStarting: " + numPackages + " packages");
298        }
299
300        public void onUpdate(int nowBeingRestored, String currentPackage) {
301            System.out.println("onUpdate: " + nowBeingRestored + " = " + currentPackage);
302        }
303
304        public void restoreFinished(int error) {
305            System.out.println("restoreFinished: " + error);
306            synchronized (this) {
307                done = true;
308                this.notify();
309            }
310        }
311
312        public void waitForCompletion() {
313            // The restoreFinished() callback will throw the 'done' flag; we
314            // just sit and wait on that notification.
315            synchronized (this) {
316                while (!this.done) {
317                    try {
318                        this.wait();
319                    } catch (InterruptedException ex) {
320                    }
321                }
322            }
323        }
324    }
325
326    private void doRestore() {
327        String arg = nextArg();
328        if (arg == null) {
329            showUsage();
330            return;
331        }
332
333        if (arg.indexOf('.') >= 0) {
334            // it's a package name
335            doRestorePackage(arg);
336        } else {
337            try {
338                long token = Long.parseLong(arg, 16);
339                HashSet<String> filter = null;
340                while ((arg = nextArg()) != null) {
341                    if (filter == null) filter = new HashSet<String>();
342                    filter.add(arg);
343                }
344
345                doRestoreAll(token, filter);
346            } catch (NumberFormatException e) {
347                showUsage();
348                return;
349            }
350        }
351
352        System.out.println("done");
353    }
354
355    private void doRestorePackage(String pkg) {
356        try {
357            mRestore = mBmgr.beginRestoreSession(pkg, null);
358            if (mRestore == null) {
359                System.err.println(BMGR_NOT_RUNNING_ERR);
360                return;
361            }
362
363            RestoreObserver observer = new RestoreObserver();
364            int err = mRestore.restorePackage(pkg, observer);
365            if (err == 0) {
366                // Off and running -- wait for the restore to complete
367                observer.waitForCompletion();
368            } else {
369                System.err.println("Unable to restore package " + pkg);
370            }
371
372            // And finally shut down the session
373            mRestore.endRestoreSession();
374        } catch (RemoteException e) {
375            System.err.println(e.toString());
376            System.err.println(BMGR_NOT_RUNNING_ERR);
377        }
378    }
379
380    private void doRestoreAll(long token, HashSet<String> filter) {
381        RestoreObserver observer = new RestoreObserver();
382
383        try {
384            boolean didRestore = false;
385            mRestore = mBmgr.beginRestoreSession(null, null);
386            if (mRestore == null) {
387                System.err.println(BMGR_NOT_RUNNING_ERR);
388                return;
389            }
390            RestoreSet[] sets = null;
391            int err = mRestore.getAvailableRestoreSets(observer);
392            if (err == 0) {
393                observer.waitForCompletion();
394                sets = observer.sets;
395                if (sets != null) {
396                    for (RestoreSet s : sets) {
397                        if (s.token == token) {
398                            System.out.println("Scheduling restore: " + s.name);
399                            if (filter == null) {
400                                didRestore = (mRestore.restoreAll(token, observer) == 0);
401                            } else {
402                                String[] names = new String[filter.size()];
403                                filter.toArray(names);
404                                didRestore = (mRestore.restoreSome(token, observer, names) == 0);
405                            }
406                            break;
407                        }
408                    }
409                }
410            }
411            if (!didRestore) {
412                if (sets == null || sets.length == 0) {
413                    System.out.println("No available restore sets; no restore performed");
414                } else {
415                    System.out.println("No matching restore set token.  Available sets:");
416                    printRestoreSets(sets);
417                }
418            }
419
420            // if we kicked off a restore successfully, we have to wait for it
421            // to complete before we can shut down the restore session safely
422            if (didRestore) {
423                observer.waitForCompletion();
424            }
425
426            // once the restore has finished, close down the session and we're done
427            mRestore.endRestoreSession();
428        } catch (RemoteException e) {
429            System.err.println(e.toString());
430            System.err.println(BMGR_NOT_RUNNING_ERR);
431        }
432    }
433
434    private String nextArg() {
435        if (mNextArg >= mArgs.length) {
436            return null;
437        }
438        String arg = mArgs[mNextArg];
439        mNextArg++;
440        return arg;
441    }
442
443    private static void showUsage() {
444        System.err.println("usage: bmgr [backup|restore|list|transport|run]");
445        System.err.println("       bmgr backup PACKAGE");
446        System.err.println("       bmgr enable BOOL");
447        System.err.println("       bmgr enabled");
448        System.err.println("       bmgr list transports");
449        System.err.println("       bmgr list sets");
450        System.err.println("       bmgr transport WHICH");
451        System.err.println("       bmgr restore TOKEN");
452        System.err.println("       bmgr restore TOKEN PACKAGE...");
453        System.err.println("       bmgr restore PACKAGE");
454        System.err.println("       bmgr run");
455        System.err.println("       bmgr wipe TRANSPORT PACKAGE");
456        System.err.println("");
457        System.err.println("The 'backup' command schedules a backup pass for the named package.");
458        System.err.println("Note that the backup pass will effectively be a no-op if the package");
459        System.err.println("does not actually have changed data to store.");
460        System.err.println("");
461        System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
462        System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
463        System.err.println("disabled.  When disabled, neither backup or restore operations will");
464        System.err.println("be performed.");
465        System.err.println("");
466        System.err.println("The 'enabled' command reports the current enabled/disabled state of");
467        System.err.println("the backup mechanism.");
468        System.err.println("");
469        System.err.println("The 'list transports' command reports the names of the backup transports");
470        System.err.println("currently available on the device.  These names can be passed as arguments");
471        System.err.println("to the 'transport' and 'wipe' commands.  The currently selected transport");
472        System.err.println("is indicated with a '*' character.");
473        System.err.println("");
474        System.err.println("The 'list sets' command reports the token and name of each restore set");
475        System.err.println("available to the device via the current transport.");
476        System.err.println("");
477        System.err.println("The 'transport' command designates the named transport as the currently");
478        System.err.println("active one.  This setting is persistent across reboots.");
479        System.err.println("");
480        System.err.println("The 'restore' command when given just a restore token initiates a full-system");
481        System.err.println("restore operation from the currently active transport.  It will deliver");
482        System.err.println("the restore set designated by the TOKEN argument to each application");
483        System.err.println("that had contributed data to that restore set.");
484        System.err.println("");
485        System.err.println("The 'restore' command when given a token and one or more package names");
486        System.err.println("initiates a restore operation of just those given packages from the restore");
487        System.err.println("set designated by the TOKEN argument.  It is effectively the same as the");
488        System.err.println("'restore' operation supplying only a token, but applies a filter to the");
489        System.err.println("set of applications to be restored.");
490        System.err.println("");
491        System.err.println("The 'restore' command when given just a package name intiates a restore of");
492        System.err.println("just that one package according to the restore set selection algorithm");
493        System.err.println("used by the RestoreSession.restorePackage() method.");
494        System.err.println("");
495        System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
496        System.err.println("immediately, without the usual waiting period for batching together");
497        System.err.println("data changes.");
498        System.err.println("");
499        System.err.println("The 'wipe' command causes all backed-up data for the given package to be");
500        System.err.println("erased from the given transport's storage.  The next backup operation");
501        System.err.println("that the given application performs will rewrite its entire data set.");
502        System.err.println("Transport names to use here are those reported by 'list transports'.");
503    }
504}
505