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 main(String[] args)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 run(String[] args)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 enableToString(boolean enabled)134 private String enableToString(boolean enabled) { 135 return enabled ? "enabled" : "disabled"; 136 } 137 doEnabled()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 doEnable()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 doRun()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 doBackup()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 doFullTransportBackup()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 onUpdate(String currentPackage, BackupProgress backupProgress)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 onResult(String currentPackage, int status)222 public void onResult(String currentPackage, int status) { 223 System.out.println("Package " + currentPackage + " with result: " 224 + convertBackupStatusToString(status)); 225 } 226 227 @Override backupFinished(int status)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 waitForCompletion()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 convertBackupStatusToString(int errorCode)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 backupNowAllPackages()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 backupNowPackages(List<String> packages)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 doBackupNow()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 doTransport()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 doWipe()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 doList()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 doListTransports()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 doListRestoreSets()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 printRestoreSets(RestoreSet[] sets)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 restoreSetsAvailable(RestoreSet[] result)466 public void restoreSetsAvailable(RestoreSet[] result) { 467 synchronized (this) { 468 sets = result; 469 done = true; 470 this.notify(); 471 } 472 } 473 restoreStarting(int numPackages)474 public void restoreStarting(int numPackages) { 475 System.out.println("restoreStarting: " + numPackages + " packages"); 476 } 477 onUpdate(int nowBeingRestored, String currentPackage)478 public void onUpdate(int nowBeingRestored, String currentPackage) { 479 System.out.println("onUpdate: " + nowBeingRestored + " = " + currentPackage); 480 } 481 restoreFinished(int error)482 public void restoreFinished(int error) { 483 System.out.println("restoreFinished: " + error); 484 synchronized (this) { 485 done = true; 486 this.notify(); 487 } 488 } 489 waitForCompletion()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 doRestore()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 doRestorePackage(String pkg)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 doRestoreAll(long token, HashSet<String> filter)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 doPrintWhitelist()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 nextArg()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 showUsage()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