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