Home | History | Annotate | Download | only in bmgr
      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 
     17 package com.android.commands.bmgr;
     18 
     19 import android.app.backup.BackupManager;
     20 import android.app.backup.BackupProgress;
     21 import android.app.backup.BackupTransport;
     22 import android.app.backup.IBackupManager;
     23 import android.app.backup.IBackupObserver;
     24 import android.app.backup.IRestoreObserver;
     25 import android.app.backup.IRestoreSession;
     26 import android.app.backup.ISelectBackupTransportCallback;
     27 import android.app.backup.RestoreSet;
     28 import android.content.ComponentName;
     29 import android.content.pm.IPackageManager;
     30 import android.content.pm.PackageInfo;
     31 import android.os.RemoteException;
     32 import android.os.ServiceManager;
     33 import android.os.SystemClock;
     34 import android.os.UserHandle;
     35 import android.util.ArraySet;
     36 
     37 import com.android.internal.annotations.GuardedBy;
     38 
     39 import java.util.ArrayList;
     40 import java.util.Arrays;
     41 import java.util.HashSet;
     42 import java.util.List;
     43 import java.util.concurrent.CountDownLatch;
     44 
     45 public 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