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