Bmgr.java revision fa51853ae56ed74a0c854c01851cb1435453005f
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        }
301    }
302
303    private static String convertBackupStatusToString(int errorCode) {
304        switch (errorCode) {
305            case BackupManager.SUCCESS:
306                return "Success";
307            case BackupManager.ERROR_BACKUP_NOT_ALLOWED:
308                return "Backup is not allowed";
309            case BackupManager.ERROR_PACKAGE_NOT_FOUND:
310                return "Package not found";
311            case BackupManager.ERROR_TRANSPORT_ABORTED:
312                return "Transport error";
313            case BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED:
314                return "Transport rejected package because it wasn't able to process it"
315                        + " at the time";
316            case BackupManager.ERROR_AGENT_FAILURE:
317                return "Agent error";
318            case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
319                return "Size quota exceeded";
320            case BackupManager.ERROR_BACKUP_CANCELLED:
321                return "Backup Cancelled";
322            default:
323                return "Unknown error";
324        }
325    }
326
327    private void backupNowAllPackages(boolean nonIncrementalBackup) {
328        int userId = UserHandle.USER_SYSTEM;
329        IPackageManager mPm =
330                IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
331        if (mPm == null) {
332            System.err.println(PM_NOT_RUNNING_ERR);
333            return;
334        }
335        List<PackageInfo> installedPackages = null;
336        try {
337            installedPackages =  mPm.getInstalledPackages(0, userId).getList();
338        } catch (RemoteException e) {
339            System.err.println(e.toString());
340            System.err.println(PM_NOT_RUNNING_ERR);
341        }
342        if (installedPackages != null) {
343            String[] packages =
344                    installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
345            String[] filteredPackages = {};
346            try {
347                filteredPackages = mBmgr.filterAppsEligibleForBackup(packages);
348            } catch (RemoteException e) {
349                System.err.println(e.toString());
350                System.err.println(BMGR_NOT_RUNNING_ERR);
351            }
352            backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup);
353        }
354    }
355
356    private void backupNowPackages(List<String> packages, boolean nonIncrementalBackup) {
357        int flags = 0;
358        if (nonIncrementalBackup) {
359            flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
360        }
361        try {
362            BackupObserver observer = new BackupObserver();
363            // TODO: implement monitor here?
364            int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer,
365                    null, flags);
366            if (err == 0) {
367                // Off and running -- wait for the backup to complete
368                observer.waitForCompletion();
369            } else {
370                System.err.println("Unable to run backup");
371            }
372        } catch (RemoteException e) {
373            System.err.println(e.toString());
374            System.err.println(BMGR_NOT_RUNNING_ERR);
375        }
376    }
377
378    private void doBackupNow() {
379        String pkg;
380        boolean backupAll = false;
381        boolean nonIncrementalBackup = false;
382        ArrayList<String> allPkgs = new ArrayList<String>();
383        while ((pkg = nextArg()) != null) {
384            if (pkg.equals("--all")) {
385                backupAll = true;
386            } else if (pkg.equals("--non-incremental")) {
387                nonIncrementalBackup = true;
388            } else if (pkg.equals("--incremental")) {
389                nonIncrementalBackup = false;
390            } else {
391                if (!allPkgs.contains(pkg)) {
392                    allPkgs.add(pkg);
393                }
394            }
395        }
396        if (backupAll) {
397            if (allPkgs.size() == 0) {
398                System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
399                        "incremental backup for all packages.");
400                backupNowAllPackages(nonIncrementalBackup);
401            } else {
402                System.err.println("Provide only '--all' flag or list of packages.");
403            }
404        } else if (allPkgs.size() > 0) {
405            System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
406                    "incremental backup for " + allPkgs.size() +" requested packages.");
407            backupNowPackages(allPkgs, nonIncrementalBackup);
408        } else {
409            System.err.println("Provide '--all' flag or list of packages.");
410        }
411    }
412
413    private void doCancel() {
414        String arg = nextArg();
415        if ("backups".equals(arg)) {
416            try {
417                mBmgr.cancelBackups();
418            } catch (RemoteException e) {
419                System.err.println(e.toString());
420                System.err.println(BMGR_NOT_RUNNING_ERR);
421            }
422            return;
423        }
424
425        System.err.println("Unknown command.");
426    }
427
428    private void doTransport() {
429        try {
430            String which = nextArg();
431            if (which == null) {
432                showUsage();
433                return;
434            }
435
436            if ("-c".equals(which)) {
437                doTransportByComponent();
438                return;
439            }
440
441            String old = mBmgr.selectBackupTransport(which);
442            if (old == null) {
443                System.out.println("Unknown transport '" + which
444                        + "' specified; no changes made.");
445            } else {
446                System.out.println("Selected transport " + which + " (formerly " + old + ")");
447            }
448
449        } catch (RemoteException e) {
450            System.err.println(e.toString());
451            System.err.println(BMGR_NOT_RUNNING_ERR);
452        }
453    }
454
455    private void doTransportByComponent() {
456        String which = nextArg();
457        if (which == null) {
458            showUsage();
459            return;
460        }
461
462        final CountDownLatch latch = new CountDownLatch(1);
463
464        try {
465            mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
466                    new ISelectBackupTransportCallback.Stub() {
467                        @Override
468                        public void onSuccess(String transportName) {
469                            System.out.println("Success. Selected transport: " + transportName);
470                            latch.countDown();
471                        }
472
473                        @Override
474                        public void onFailure(int reason) {
475                            System.err.println("Failure. error=" + reason);
476                            latch.countDown();
477                        }
478                    });
479        } catch (RemoteException e) {
480            System.err.println(e.toString());
481            System.err.println(BMGR_NOT_RUNNING_ERR);
482            return;
483        }
484
485        try {
486            latch.await();
487        } catch (InterruptedException e) {
488            System.err.println("Operation interrupted.");
489        }
490    }
491
492    private void doWipe() {
493        String transport = nextArg();
494        if (transport == null) {
495            showUsage();
496            return;
497        }
498
499        String pkg = nextArg();
500        if (pkg == null) {
501            showUsage();
502            return;
503        }
504
505        try {
506            mBmgr.clearBackupData(transport, pkg);
507            System.out.println("Wiped backup data for " + pkg + " on " + transport);
508        } catch (RemoteException e) {
509            System.err.println(e.toString());
510            System.err.println(BMGR_NOT_RUNNING_ERR);
511        }
512    }
513
514    class InitObserver extends Observer {
515        public int result = BackupTransport.TRANSPORT_ERROR;
516
517        @Override
518        public void backupFinished(int status) {
519            super.backupFinished(status);
520            result = status;
521        }
522    }
523
524    private void doInit() {
525        ArraySet<String> transports = new ArraySet<>();
526        String transport;
527        while ((transport = nextArg()) != null) {
528            transports.add(transport);
529        }
530        if (transports.size() == 0) {
531            showUsage();
532            return;
533        }
534
535        InitObserver observer = new InitObserver();
536        try {
537            System.out.println("Initializing transports: " + transports);
538            mBmgr.initializeTransports(transports.toArray(new String[transports.size()]), observer);
539            observer.waitForCompletion(30*1000L);
540            System.out.println("Initialization result: " + observer.result);
541        } catch (RemoteException e) {
542            System.err.println(e.toString());
543            System.err.println(BMGR_NOT_RUNNING_ERR);
544        }
545    }
546
547    private void doList() {
548        String arg = nextArg();     // sets, transports, packages set#
549        if ("transports".equals(arg)) {
550            doListTransports();
551            return;
552        }
553
554        // The rest of the 'list' options work with a restore session on the current transport
555        try {
556            mRestore = mBmgr.beginRestoreSession(null, null);
557            if (mRestore == null) {
558                System.err.println(BMGR_NOT_RUNNING_ERR);
559                return;
560            }
561
562            if ("sets".equals(arg)) {
563                doListRestoreSets();
564            } else if ("transports".equals(arg)) {
565                doListTransports();
566            }
567
568            mRestore.endRestoreSession();
569        } catch (RemoteException e) {
570            System.err.println(e.toString());
571            System.err.println(BMGR_NOT_RUNNING_ERR);
572        }
573    }
574
575    private void doListTransports() {
576        String arg = nextArg();
577
578        try {
579            if ("-c".equals(arg)) {
580                for (ComponentName transport : mBmgr.listAllTransportComponents()) {
581                    System.out.println(transport.flattenToShortString());
582                }
583                return;
584            }
585
586            String current = mBmgr.getCurrentTransport();
587            String[] transports = mBmgr.listAllTransports();
588            if (transports == null || transports.length == 0) {
589                System.out.println("No transports available.");
590                return;
591            }
592
593            for (String t : transports) {
594                String pad = (t.equals(current)) ? "  * " : "    ";
595                System.out.println(pad + t);
596            }
597        } catch (RemoteException e) {
598            System.err.println(e.toString());
599            System.err.println(BMGR_NOT_RUNNING_ERR);
600        }
601    }
602
603    private void doListRestoreSets() {
604        try {
605            RestoreObserver observer = new RestoreObserver();
606            // TODO implement monitor here
607            int err = mRestore.getAvailableRestoreSets(observer, null);
608            if (err != 0) {
609                System.out.println("Unable to request restore sets");
610            } else {
611                observer.waitForCompletion();
612                printRestoreSets(observer.sets);
613            }
614        } catch (RemoteException e) {
615            System.err.println(e.toString());
616            System.err.println(TRANSPORT_NOT_RUNNING_ERR);
617        }
618    }
619
620    private void printRestoreSets(RestoreSet[] sets) {
621        if (sets == null || sets.length == 0) {
622            System.out.println("No restore sets");
623            return;
624        }
625        for (RestoreSet s : sets) {
626            System.out.println("  " + Long.toHexString(s.token) + " : " + s.name);
627        }
628    }
629
630    class RestoreObserver extends IRestoreObserver.Stub {
631        boolean done;
632        RestoreSet[] sets = null;
633
634        public void restoreSetsAvailable(RestoreSet[] result) {
635            synchronized (this) {
636                sets = result;
637                done = true;
638                this.notify();
639            }
640        }
641
642        public void restoreStarting(int numPackages) {
643            System.out.println("restoreStarting: " + numPackages + " packages");
644        }
645
646        public void onUpdate(int nowBeingRestored, String currentPackage) {
647            System.out.println("onUpdate: " + nowBeingRestored + " = " + currentPackage);
648        }
649
650        public void restoreFinished(int error) {
651            System.out.println("restoreFinished: " + error);
652            synchronized (this) {
653                done = true;
654                this.notify();
655            }
656        }
657
658        /**
659         * Wait until either {@link #restoreFinished} or {@link #restoreStarting} is called.
660         * Once one is called, it clears the internal flag again, so that the same observer intance
661         * can be reused for a next operation.
662         */
663        public void waitForCompletion() {
664            // The restoreFinished() callback will throw the 'done' flag; we
665            // just sit and wait on that notification.
666            synchronized (this) {
667                while (!this.done) {
668                    try {
669                        this.wait();
670                    } catch (InterruptedException ex) {
671                    }
672                }
673                done = false;
674            }
675        }
676    }
677
678    private void doRestore() {
679        String arg = nextArg();
680        if (arg == null) {
681            showUsage();
682            return;
683        }
684
685        if (arg.indexOf('.') >= 0 || arg.equals("android")) {
686            // it's a package name
687            doRestorePackage(arg);
688        } else {
689            try {
690                long token = Long.parseLong(arg, 16);
691                HashSet<String> filter = null;
692                while ((arg = nextArg()) != null) {
693                    if (filter == null) filter = new HashSet<String>();
694                    filter.add(arg);
695                }
696
697                doRestoreAll(token, filter);
698            } catch (NumberFormatException e) {
699                showUsage();
700                return;
701            }
702        }
703
704        System.out.println("done");
705    }
706
707    private void doRestorePackage(String pkg) {
708        try {
709            mRestore = mBmgr.beginRestoreSession(pkg, null);
710            if (mRestore == null) {
711                System.err.println(BMGR_NOT_RUNNING_ERR);
712                return;
713            }
714
715            RestoreObserver observer = new RestoreObserver();
716            // TODO implement monitor here
717            int err = mRestore.restorePackage(pkg, observer, null );
718            if (err == 0) {
719                // Off and running -- wait for the restore to complete
720                observer.waitForCompletion();
721            } else {
722                System.err.println("Unable to restore package " + pkg);
723            }
724
725            // And finally shut down the session
726            mRestore.endRestoreSession();
727        } catch (RemoteException e) {
728            System.err.println(e.toString());
729            System.err.println(BMGR_NOT_RUNNING_ERR);
730        }
731    }
732
733    private void doRestoreAll(long token, HashSet<String> filter) {
734        RestoreObserver observer = new RestoreObserver();
735
736        try {
737            boolean didRestore = false;
738            mRestore = mBmgr.beginRestoreSession(null, null);
739            if (mRestore == null) {
740                System.err.println(BMGR_NOT_RUNNING_ERR);
741                return;
742            }
743            RestoreSet[] sets = null;
744            // TODO implement monitor here
745            int err = mRestore.getAvailableRestoreSets(observer, null);
746            if (err == 0) {
747                observer.waitForCompletion();
748                sets = observer.sets;
749                if (sets != null) {
750                    for (RestoreSet s : sets) {
751                        if (s.token == token) {
752                            System.out.println("Scheduling restore: " + s.name);
753                            if (filter == null) {
754                                didRestore = (mRestore.restoreAll(token, observer, null) == 0);
755                            } else {
756                                String[] names = new String[filter.size()];
757                                filter.toArray(names);
758                                didRestore = (mRestore.restoreSome(token, observer,
759                                        null, names) == 0);
760                            }
761                            break;
762                        }
763                    }
764                }
765            }
766            if (!didRestore) {
767                if (sets == null || sets.length == 0) {
768                    System.out.println("No available restore sets; no restore performed");
769                } else {
770                    System.out.println("No matching restore set token.  Available sets:");
771                    printRestoreSets(sets);
772                }
773            }
774
775            // if we kicked off a restore successfully, we have to wait for it
776            // to complete before we can shut down the restore session safely
777            if (didRestore) {
778                observer.waitForCompletion();
779            }
780
781            // once the restore has finished, close down the session and we're done
782            mRestore.endRestoreSession();
783        } catch (RemoteException e) {
784            System.err.println(e.toString());
785            System.err.println(BMGR_NOT_RUNNING_ERR);
786        }
787    }
788
789    private void doPrintWhitelist() {
790        try {
791            final String[] whitelist = mBmgr.getTransportWhitelist();
792            if (whitelist != null) {
793                for (String transport : whitelist) {
794                    System.out.println(transport);
795                }
796            }
797        } catch (RemoteException e) {
798            System.err.println(e.toString());
799            System.err.println(BMGR_NOT_RUNNING_ERR);
800        }
801    }
802
803    private String nextArg() {
804        if (mNextArg >= mArgs.length) {
805            return null;
806        }
807        String arg = mArgs[mNextArg];
808        mNextArg++;
809        return arg;
810    }
811
812    private static void showUsage() {
813        System.err.println("usage: bmgr [backup|restore|list|transport|run]");
814        System.err.println("       bmgr backup PACKAGE");
815        System.err.println("       bmgr enable BOOL");
816        System.err.println("       bmgr enabled");
817        System.err.println("       bmgr list transports [-c]");
818        System.err.println("       bmgr list sets");
819        System.err.println("       bmgr transport WHICH|-c WHICH_COMPONENT");
820        System.err.println("       bmgr restore TOKEN");
821        System.err.println("       bmgr restore TOKEN PACKAGE...");
822        System.err.println("       bmgr restore PACKAGE");
823        System.err.println("       bmgr run");
824        System.err.println("       bmgr wipe TRANSPORT PACKAGE");
825        System.err.println("       bmgr fullbackup PACKAGE...");
826        System.err.println("       bmgr backupnow --all|PACKAGE...");
827        System.err.println("       bmgr cancel backups");
828        System.err.println("");
829        System.err.println("The 'backup' command schedules a backup pass for the named package.");
830        System.err.println("Note that the backup pass will effectively be a no-op if the package");
831        System.err.println("does not actually have changed data to store.");
832        System.err.println("");
833        System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
834        System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
835        System.err.println("disabled.  When disabled, neither backup or restore operations will");
836        System.err.println("be performed.");
837        System.err.println("");
838        System.err.println("The 'enabled' command reports the current enabled/disabled state of");
839        System.err.println("the backup mechanism.");
840        System.err.println("");
841        System.err.println("The 'list transports' command reports the names of the backup transports");
842        System.err.println("BackupManager is currently bound to. These names can be passed as arguments");
843        System.err.println("to the 'transport' and 'wipe' commands.  The currently active transport");
844        System.err.println("is indicated with a '*' character. If -c flag is used, all available");
845        System.err.println("transport components on the device are listed. These can be used with");
846        System.err.println("the component variant of 'transport' command.");
847        System.err.println("");
848        System.err.println("The 'list sets' command reports the token and name of each restore set");
849        System.err.println("available to the device via the currently active transport.");
850        System.err.println("");
851        System.err.println("The 'transport' command designates the named transport as the currently");
852        System.err.println("active one.  This setting is persistent across reboots. If -c flag is");
853        System.err.println("specified, the following string is treated as a component name.");
854        System.err.println("");
855        System.err.println("The 'restore' command when given just a restore token initiates a full-system");
856        System.err.println("restore operation from the currently active transport.  It will deliver");
857        System.err.println("the restore set designated by the TOKEN argument to each application");
858        System.err.println("that had contributed data to that restore set.");
859        System.err.println("");
860        System.err.println("The 'restore' command when given a token and one or more package names");
861        System.err.println("initiates a restore operation of just those given packages from the restore");
862        System.err.println("set designated by the TOKEN argument.  It is effectively the same as the");
863        System.err.println("'restore' operation supplying only a token, but applies a filter to the");
864        System.err.println("set of applications to be restored.");
865        System.err.println("");
866        System.err.println("The 'restore' command when given just a package name intiates a restore of");
867        System.err.println("just that one package according to the restore set selection algorithm");
868        System.err.println("used by the RestoreSession.restorePackage() method.");
869        System.err.println("");
870        System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
871        System.err.println("immediately, without the usual waiting period for batching together");
872        System.err.println("data changes.");
873        System.err.println("");
874        System.err.println("The 'wipe' command causes all backed-up data for the given package to be");
875        System.err.println("erased from the given transport's storage.  The next backup operation");
876        System.err.println("that the given application performs will rewrite its entire data set.");
877        System.err.println("Transport names to use here are those reported by 'list transports'.");
878        System.err.println("");
879        System.err.println("The 'fullbackup' command induces a full-data stream backup for one or more");
880        System.err.println("packages.  The data is sent via the currently active transport.");
881        System.err.println("");
882        System.err.println("The 'backupnow' command runs an immediate backup for one or more packages.");
883        System.err.println("    --all flag runs backup for all eligible packages.");
884        System.err.println("For each package it will run key/value or full data backup ");
885        System.err.println("depending on the package's manifest declarations.");
886        System.err.println("The data is sent via the currently active transport.");
887        System.err.println("The 'cancel backups' command cancels all running backups.");
888    }
889}
890