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