Bmgr.java revision 865303fce57b968f5bd7efb4dd23ccb1a3747b93
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.BackupManager;
20import android.app.backup.BackupProgress;
21import android.app.backup.IBackupManager;
22import android.app.backup.IBackupObserver;
23import android.app.backup.IRestoreObserver;
24import android.app.backup.IRestoreSession;
25import android.app.backup.RestoreSet;
26import android.app.backup.ISelectBackupTransportCallback;
27import android.content.ComponentName;
28import android.content.pm.IPackageManager;
29import android.content.pm.PackageInfo;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.UserHandle;
33import android.util.Log;
34
35import java.util.ArrayList;
36import java.util.HashSet;
37import java.util.List;
38import java.util.concurrent.CountDownLatch;
39
40public final class Bmgr {
41    IBackupManager mBmgr;
42    IRestoreSession mRestore;
43
44    static final String BMGR_NOT_RUNNING_ERR =
45            "Error: Could not access the Backup Manager.  Is the system running?";
46    static final String TRANSPORT_NOT_RUNNING_ERR =
47            "Error: Could not access the backup transport.  Is the system running?";
48    static final String PM_NOT_RUNNING_ERR =
49            "Error: Could not access the Package Manager.  Is the system running?";
50
51    private String[] mArgs;
52    private int mNextArg;
53
54    public static void main(String[] args) {
55        try {
56            new Bmgr().run(args);
57        } catch (Exception e) {
58            System.err.println("Exception caught:");
59            e.printStackTrace();
60        }
61    }
62
63    public void run(String[] args) {
64        if (args.length < 1) {
65            showUsage();
66            return;
67        }
68
69        mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
70        if (mBmgr == null) {
71            System.err.println(BMGR_NOT_RUNNING_ERR);
72            return;
73        }
74
75        mArgs = args;
76        String op = args[0];
77        mNextArg = 1;
78
79        if ("enabled".equals(op)) {
80            doEnabled();
81            return;
82        }
83
84        if ("enable".equals(op)) {
85            doEnable();
86            return;
87        }
88
89        if ("run".equals(op)) {
90            doRun();
91            return;
92        }
93
94        if ("backup".equals(op)) {
95            doBackup();
96            return;
97        }
98
99        if ("list".equals(op)) {
100            doList();
101            return;
102        }
103
104        if ("restore".equals(op)) {
105            doRestore();
106            return;
107        }
108
109        if ("transport".equals(op)) {
110            doTransport();
111            return;
112        }
113
114        if ("wipe".equals(op)) {
115            doWipe();
116            return;
117        }
118
119        if ("fullbackup".equals(op)) {
120            doFullTransportBackup();
121            return;
122        }
123
124        if ("backupnow".equals(op)) {
125            doBackupNow();
126            return;
127        }
128
129        if ("whitelist".equals(op)) {
130            doPrintWhitelist();
131            return;
132        }
133
134        System.err.println("Unknown command");
135        showUsage();
136    }
137
138    private String enableToString(boolean enabled) {
139        return enabled ? "enabled" : "disabled";
140    }
141
142    private void doEnabled() {
143        try {
144            boolean isEnabled = mBmgr.isBackupEnabled();
145            System.out.println("Backup Manager currently "
146                    + enableToString(isEnabled));
147        } catch (RemoteException e) {
148            System.err.println(e.toString());
149            System.err.println(BMGR_NOT_RUNNING_ERR);
150        }
151    }
152
153    private void doEnable() {
154        String arg = nextArg();
155        if (arg == null) {
156            showUsage();
157            return;
158        }
159
160        try {
161            boolean enable = Boolean.parseBoolean(arg);
162            mBmgr.setBackupEnabled(enable);
163            System.out.println("Backup Manager now " + enableToString(enable));
164        } catch (NumberFormatException e) {
165            showUsage();
166            return;
167        } catch (RemoteException e) {
168            System.err.println(e.toString());
169            System.err.println(BMGR_NOT_RUNNING_ERR);
170        }
171    }
172
173    private void doRun() {
174        try {
175            mBmgr.backupNow();
176        } catch (RemoteException e) {
177            System.err.println(e.toString());
178            System.err.println(BMGR_NOT_RUNNING_ERR);
179        }
180    }
181
182    private void doBackup() {
183        String pkg = nextArg();
184        if (pkg == null) {
185            showUsage();
186            return;
187        }
188
189        try {
190            mBmgr.dataChanged(pkg);
191        } catch (RemoteException e) {
192            System.err.println(e.toString());
193            System.err.println(BMGR_NOT_RUNNING_ERR);
194        }
195    }
196
197    private void doFullTransportBackup() {
198        System.out.println("Performing full transport backup");
199
200        String pkg;
201        ArrayList<String> allPkgs = new ArrayList<String>();
202        while ((pkg = nextArg()) != null) {
203            allPkgs.add(pkg);
204        }
205        if (allPkgs.size() > 0) {
206            try {
207                mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
208            } catch (RemoteException e) {
209                System.err.println(e.toString());
210                System.err.println(BMGR_NOT_RUNNING_ERR);
211            }
212        }
213    }
214
215    class BackupObserver extends IBackupObserver.Stub {
216        boolean done = false;
217
218        @Override
219        public void onUpdate(String currentPackage, BackupProgress backupProgress) {
220            System.out.println(
221                "Package " + currentPackage + " with progress: " + backupProgress.bytesTransferred
222                    + "/" + backupProgress.bytesExpected);
223        }
224
225        @Override
226        public void onResult(String currentPackage, int status) {
227            System.out.println("Package " + currentPackage + " with result: "
228                    + convertBackupStatusToString(status));
229        }
230
231        @Override
232        public void backupFinished(int status) {
233            System.out.println("Backup finished with result: "
234                    + convertBackupStatusToString(status));
235            synchronized (this) {
236                done = true;
237                this.notify();
238            }
239        }
240
241        public void waitForCompletion() {
242            // The backupFinished() callback will throw the 'done' flag; we
243            // just sit and wait on that notification.
244            synchronized (this) {
245                while (!this.done) {
246                    try {
247                        this.wait();
248                    } catch (InterruptedException ex) {
249                    }
250                }
251            }
252        }
253
254    }
255
256    private static String convertBackupStatusToString(int errorCode) {
257        switch (errorCode) {
258            case BackupManager.SUCCESS:
259                return "Success";
260            case BackupManager.ERROR_BACKUP_NOT_ALLOWED:
261                return "Backup is not allowed";
262            case BackupManager.ERROR_PACKAGE_NOT_FOUND:
263                return "Package not found";
264            case BackupManager.ERROR_TRANSPORT_ABORTED:
265                return "Transport error";
266            case BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED:
267                return "Transport rejected package";
268            case BackupManager.ERROR_AGENT_FAILURE:
269                return "Agent error";
270            case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
271                return "Size quota exceeded";
272            default:
273                return "Unknown error";
274        }
275    }
276
277    private void backupNowAllPackages(boolean nonIncrementalBackup) {
278        int userId = UserHandle.USER_SYSTEM;
279        IPackageManager mPm =
280                IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
281        if (mPm == null) {
282            System.err.println(PM_NOT_RUNNING_ERR);
283            return;
284        }
285        List<PackageInfo> installedPackages = null;
286        try {
287            installedPackages =  mPm.getInstalledPackages(0, userId).getList();
288        } catch (RemoteException e) {
289            System.err.println(e.toString());
290            System.err.println(PM_NOT_RUNNING_ERR);
291        }
292        if (installedPackages != null) {
293            List<String> packages = new ArrayList<>();
294            for (PackageInfo pi : installedPackages) {
295                try {
296                    if (mBmgr.isAppEligibleForBackup(pi.packageName)) {
297                        packages.add(pi.packageName);
298                    }
299                } catch (RemoteException e) {
300                    System.err.println(e.toString());
301                    System.err.println(BMGR_NOT_RUNNING_ERR);
302                }
303            }
304            backupNowPackages(packages, nonIncrementalBackup);
305        }
306    }
307
308    private void backupNowPackages(List<String> packages, boolean nonIncrementalBackup) {
309        int flags = 0;
310        if (nonIncrementalBackup) {
311            flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
312        }
313        try {
314            BackupObserver observer = new BackupObserver();
315            int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer,
316                    flags);
317            if (err == 0) {
318                // Off and running -- wait for the backup to complete
319                observer.waitForCompletion();
320            } else {
321                System.err.println("Unable to run backup");
322            }
323        } catch (RemoteException e) {
324            System.err.println(e.toString());
325            System.err.println(BMGR_NOT_RUNNING_ERR);
326        }
327    }
328
329    private void doBackupNow() {
330        String pkg;
331        boolean backupAll = false;
332        boolean nonIncrementalBackup = false;
333        ArrayList<String> allPkgs = new ArrayList<String>();
334        while ((pkg = nextArg()) != null) {
335            if (pkg.equals("--all")) {
336                backupAll = true;
337            } else if (pkg.equals("--non-incremental")) {
338                nonIncrementalBackup = true;
339            } else if (pkg.equals("--incremental")) {
340                nonIncrementalBackup = false;
341            } else {
342                allPkgs.add(pkg);
343            }
344        }
345        if (backupAll) {
346            if (allPkgs.size() == 0) {
347                System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
348                        "incremental backup for all packages.");
349                backupNowAllPackages(nonIncrementalBackup);
350            } else {
351                System.err.println("Provide only '--all' flag or list of packages.");
352            }
353        } else if (allPkgs.size() > 0) {
354            System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
355                    "incremental backup for " + allPkgs.size() +" requested packages.");
356            backupNowPackages(allPkgs, nonIncrementalBackup);
357        } else {
358            System.err.println("Provide '--all' flag or list of packages.");
359        }
360    }
361
362    private void doTransport() {
363        try {
364            String which = nextArg();
365            if (which == null) {
366                showUsage();
367                return;
368            }
369
370            if ("-c".equals(which)) {
371                doTransportByComponent();
372                return;
373            }
374
375            String old = mBmgr.selectBackupTransport(which);
376            if (old == null) {
377                System.out.println("Unknown transport '" + which
378                        + "' specified; no changes made.");
379            } else {
380                System.out.println("Selected transport " + which + " (formerly " + old + ")");
381            }
382
383        } catch (RemoteException e) {
384            System.err.println(e.toString());
385            System.err.println(BMGR_NOT_RUNNING_ERR);
386        }
387    }
388
389    private void doTransportByComponent() {
390        String which = nextArg();
391        if (which == null) {
392            showUsage();
393            return;
394        }
395
396        final CountDownLatch latch = new CountDownLatch(1);
397
398        try {
399            mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
400                    new ISelectBackupTransportCallback.Stub() {
401                        @Override
402                        public void onSuccess(String transportName) {
403                            System.out.println("Success. Selected transport: " + transportName);
404                            latch.countDown();
405                        }
406
407                        @Override
408                        public void onFailure(int reason) {
409                            System.err.println("Failure. error=" + reason);
410                            latch.countDown();
411                        }
412                    });
413        } catch (RemoteException e) {
414            System.err.println(e.toString());
415            System.err.println(BMGR_NOT_RUNNING_ERR);
416            return;
417        }
418
419        try {
420            latch.await();
421        } catch (InterruptedException e) {
422            System.err.println("Operation interrupted.");
423        }
424    }
425
426    private void doWipe() {
427        String transport = nextArg();
428        if (transport == null) {
429            showUsage();
430            return;
431        }
432
433        String pkg = nextArg();
434        if (pkg == null) {
435            showUsage();
436            return;
437        }
438
439        try {
440            mBmgr.clearBackupData(transport, pkg);
441            System.out.println("Wiped backup data for " + pkg + " on " + transport);
442        } catch (RemoteException e) {
443            System.err.println(e.toString());
444            System.err.println(BMGR_NOT_RUNNING_ERR);
445        }
446    }
447
448    private void doList() {
449        String arg = nextArg();     // sets, transports, packages set#
450        if ("transports".equals(arg)) {
451            doListTransports();
452            return;
453        }
454
455        // The rest of the 'list' options work with a restore session on the current transport
456        try {
457            mRestore = mBmgr.beginRestoreSession(null, null);
458            if (mRestore == null) {
459                System.err.println(BMGR_NOT_RUNNING_ERR);
460                return;
461            }
462
463            if ("sets".equals(arg)) {
464                doListRestoreSets();
465            } else if ("transports".equals(arg)) {
466                doListTransports();
467            }
468
469            mRestore.endRestoreSession();
470        } catch (RemoteException e) {
471            System.err.println(e.toString());
472            System.err.println(BMGR_NOT_RUNNING_ERR);
473        }
474    }
475
476    private void doListTransports() {
477        String arg = nextArg();
478
479        try {
480            if ("-c".equals(arg)) {
481                for (ComponentName transport : mBmgr.listAllTransportComponents()) {
482                    System.out.println(transport.flattenToShortString());
483                }
484                return;
485            }
486
487            String current = mBmgr.getCurrentTransport();
488            String[] transports = mBmgr.listAllTransports();
489            if (transports == null || transports.length == 0) {
490                System.out.println("No transports available.");
491                return;
492            }
493
494            for (String t : transports) {
495                String pad = (t.equals(current)) ? "  * " : "    ";
496                System.out.println(pad + t);
497            }
498        } catch (RemoteException e) {
499            System.err.println(e.toString());
500            System.err.println(BMGR_NOT_RUNNING_ERR);
501        }
502    }
503
504    private void doListRestoreSets() {
505        try {
506            RestoreObserver observer = new RestoreObserver();
507            int err = mRestore.getAvailableRestoreSets(observer);
508            if (err != 0) {
509                System.out.println("Unable to request restore sets");
510            } else {
511                observer.waitForCompletion();
512                printRestoreSets(observer.sets);
513            }
514        } catch (RemoteException e) {
515            System.err.println(e.toString());
516            System.err.println(TRANSPORT_NOT_RUNNING_ERR);
517        }
518    }
519
520    private void printRestoreSets(RestoreSet[] sets) {
521        if (sets == null || sets.length == 0) {
522            System.out.println("No restore sets");
523            return;
524        }
525        for (RestoreSet s : sets) {
526            System.out.println("  " + Long.toHexString(s.token) + " : " + s.name);
527        }
528    }
529
530    class RestoreObserver extends IRestoreObserver.Stub {
531        boolean done;
532        RestoreSet[] sets = null;
533
534        public void restoreSetsAvailable(RestoreSet[] result) {
535            synchronized (this) {
536                sets = result;
537                done = true;
538                this.notify();
539            }
540        }
541
542        public void restoreStarting(int numPackages) {
543            System.out.println("restoreStarting: " + numPackages + " packages");
544        }
545
546        public void onUpdate(int nowBeingRestored, String currentPackage) {
547            System.out.println("onUpdate: " + nowBeingRestored + " = " + currentPackage);
548        }
549
550        public void restoreFinished(int error) {
551            System.out.println("restoreFinished: " + error);
552            synchronized (this) {
553                done = true;
554                this.notify();
555            }
556        }
557
558        public void waitForCompletion() {
559            // The restoreFinished() callback will throw the 'done' flag; we
560            // just sit and wait on that notification.
561            synchronized (this) {
562                while (!this.done) {
563                    try {
564                        this.wait();
565                    } catch (InterruptedException ex) {
566                    }
567                }
568            }
569        }
570    }
571
572    private void doRestore() {
573        String arg = nextArg();
574        if (arg == null) {
575            showUsage();
576            return;
577        }
578
579        if (arg.indexOf('.') >= 0 || arg.equals("android")) {
580            // it's a package name
581            doRestorePackage(arg);
582        } else {
583            try {
584                long token = Long.parseLong(arg, 16);
585                HashSet<String> filter = null;
586                while ((arg = nextArg()) != null) {
587                    if (filter == null) filter = new HashSet<String>();
588                    filter.add(arg);
589                }
590
591                doRestoreAll(token, filter);
592            } catch (NumberFormatException e) {
593                showUsage();
594                return;
595            }
596        }
597
598        System.out.println("done");
599    }
600
601    private void doRestorePackage(String pkg) {
602        try {
603            mRestore = mBmgr.beginRestoreSession(pkg, null);
604            if (mRestore == null) {
605                System.err.println(BMGR_NOT_RUNNING_ERR);
606                return;
607            }
608
609            RestoreObserver observer = new RestoreObserver();
610            int err = mRestore.restorePackage(pkg, observer);
611            if (err == 0) {
612                // Off and running -- wait for the restore to complete
613                observer.waitForCompletion();
614            } else {
615                System.err.println("Unable to restore package " + pkg);
616            }
617
618            // And finally shut down the session
619            mRestore.endRestoreSession();
620        } catch (RemoteException e) {
621            System.err.println(e.toString());
622            System.err.println(BMGR_NOT_RUNNING_ERR);
623        }
624    }
625
626    private void doRestoreAll(long token, HashSet<String> filter) {
627        RestoreObserver observer = new RestoreObserver();
628
629        try {
630            boolean didRestore = false;
631            mRestore = mBmgr.beginRestoreSession(null, null);
632            if (mRestore == null) {
633                System.err.println(BMGR_NOT_RUNNING_ERR);
634                return;
635            }
636            RestoreSet[] sets = null;
637            int err = mRestore.getAvailableRestoreSets(observer);
638            if (err == 0) {
639                observer.waitForCompletion();
640                sets = observer.sets;
641                if (sets != null) {
642                    for (RestoreSet s : sets) {
643                        if (s.token == token) {
644                            System.out.println("Scheduling restore: " + s.name);
645                            if (filter == null) {
646                                didRestore = (mRestore.restoreAll(token, observer) == 0);
647                            } else {
648                                String[] names = new String[filter.size()];
649                                filter.toArray(names);
650                                didRestore = (mRestore.restoreSome(token, observer, names) == 0);
651                            }
652                            break;
653                        }
654                    }
655                }
656            }
657            if (!didRestore) {
658                if (sets == null || sets.length == 0) {
659                    System.out.println("No available restore sets; no restore performed");
660                } else {
661                    System.out.println("No matching restore set token.  Available sets:");
662                    printRestoreSets(sets);
663                }
664            }
665
666            // if we kicked off a restore successfully, we have to wait for it
667            // to complete before we can shut down the restore session safely
668            if (didRestore) {
669                observer.waitForCompletion();
670            }
671
672            // once the restore has finished, close down the session and we're done
673            mRestore.endRestoreSession();
674        } catch (RemoteException e) {
675            System.err.println(e.toString());
676            System.err.println(BMGR_NOT_RUNNING_ERR);
677        }
678    }
679
680    private void doPrintWhitelist() {
681        try {
682            final String[] whitelist = mBmgr.getTransportWhitelist();
683            if (whitelist != null) {
684                for (String transport : whitelist) {
685                    System.out.println(transport);
686                }
687            }
688        } catch (RemoteException e) {
689            System.err.println(e.toString());
690            System.err.println(BMGR_NOT_RUNNING_ERR);
691        }
692    }
693
694    private String nextArg() {
695        if (mNextArg >= mArgs.length) {
696            return null;
697        }
698        String arg = mArgs[mNextArg];
699        mNextArg++;
700        return arg;
701    }
702
703    private static void showUsage() {
704        System.err.println("usage: bmgr [backup|restore|list|transport|run]");
705        System.err.println("       bmgr backup PACKAGE");
706        System.err.println("       bmgr enable BOOL");
707        System.err.println("       bmgr enabled");
708        System.err.println("       bmgr list transports [-c]");
709        System.err.println("       bmgr list sets");
710        System.err.println("       bmgr transport WHICH|-c WHICH_COMPONENT");
711        System.err.println("       bmgr restore TOKEN");
712        System.err.println("       bmgr restore TOKEN PACKAGE...");
713        System.err.println("       bmgr restore PACKAGE");
714        System.err.println("       bmgr run");
715        System.err.println("       bmgr wipe TRANSPORT PACKAGE");
716        System.err.println("       bmgr fullbackup PACKAGE...");
717        System.err.println("       bmgr backupnow --all|PACKAGE...");
718        System.err.println("");
719        System.err.println("The 'backup' command schedules a backup pass for the named package.");
720        System.err.println("Note that the backup pass will effectively be a no-op if the package");
721        System.err.println("does not actually have changed data to store.");
722        System.err.println("");
723        System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
724        System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
725        System.err.println("disabled.  When disabled, neither backup or restore operations will");
726        System.err.println("be performed.");
727        System.err.println("");
728        System.err.println("The 'enabled' command reports the current enabled/disabled state of");
729        System.err.println("the backup mechanism.");
730        System.err.println("");
731        System.err.println("The 'list transports' command reports the names of the backup transports");
732        System.err.println("BackupManager is currently bound to. These names can be passed as arguments");
733        System.err.println("to the 'transport' and 'wipe' commands.  The currently active transport");
734        System.err.println("is indicated with a '*' character. If -c flag is used, all available");
735        System.err.println("transport components on the device are listed. These can be used with");
736        System.err.println("the component variant of 'transport' command.");
737        System.err.println("");
738        System.err.println("The 'list sets' command reports the token and name of each restore set");
739        System.err.println("available to the device via the currently active transport.");
740        System.err.println("");
741        System.err.println("The 'transport' command designates the named transport as the currently");
742        System.err.println("active one.  This setting is persistent across reboots. If -c flag is");
743        System.err.println("specified, the following string is treated as a component name.");
744        System.err.println("");
745        System.err.println("The 'restore' command when given just a restore token initiates a full-system");
746        System.err.println("restore operation from the currently active transport.  It will deliver");
747        System.err.println("the restore set designated by the TOKEN argument to each application");
748        System.err.println("that had contributed data to that restore set.");
749        System.err.println("");
750        System.err.println("The 'restore' command when given a token and one or more package names");
751        System.err.println("initiates a restore operation of just those given packages from the restore");
752        System.err.println("set designated by the TOKEN argument.  It is effectively the same as the");
753        System.err.println("'restore' operation supplying only a token, but applies a filter to the");
754        System.err.println("set of applications to be restored.");
755        System.err.println("");
756        System.err.println("The 'restore' command when given just a package name intiates a restore of");
757        System.err.println("just that one package according to the restore set selection algorithm");
758        System.err.println("used by the RestoreSession.restorePackage() method.");
759        System.err.println("");
760        System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
761        System.err.println("immediately, without the usual waiting period for batching together");
762        System.err.println("data changes.");
763        System.err.println("");
764        System.err.println("The 'wipe' command causes all backed-up data for the given package to be");
765        System.err.println("erased from the given transport's storage.  The next backup operation");
766        System.err.println("that the given application performs will rewrite its entire data set.");
767        System.err.println("Transport names to use here are those reported by 'list transports'.");
768        System.err.println("");
769        System.err.println("The 'fullbackup' command induces a full-data stream backup for one or more");
770        System.err.println("packages.  The data is sent via the currently active transport.");
771        System.err.println("");
772        System.err.println("The 'backupnow' command runs an immediate backup for one or more packages.");
773        System.err.println("    --all flag runs backup for all eligible packages.");
774        System.err.println("For each package it will run key/value or full data backup ");
775        System.err.println("depending on the package's manifest declarations.");
776        System.err.println("The data is sent via the currently active transport.");
777    }
778}
779