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.RestoreSet; 20import android.app.backup.IBackupManager; 21import android.app.backup.IRestoreObserver; 22import android.app.backup.IRestoreSession; 23import android.os.RemoteException; 24import android.os.ServiceManager; 25 26import java.util.ArrayList; 27import java.util.HashSet; 28 29public final class Bmgr { 30 IBackupManager mBmgr; 31 IRestoreSession mRestore; 32 33 static final String BMGR_NOT_RUNNING_ERR = 34 "Error: Could not access the Backup Manager. Is the system running?"; 35 static final String TRANSPORT_NOT_RUNNING_ERR = 36 "Error: Could not access the backup transport. Is the system running?"; 37 38 private String[] mArgs; 39 private int mNextArg; 40 41 public static void main(String[] args) { 42 try { 43 new Bmgr().run(args); 44 } catch (Exception e) { 45 System.err.println("Exception caught:"); 46 e.printStackTrace(); 47 } 48 } 49 50 public void run(String[] args) { 51 if (args.length < 1) { 52 showUsage(); 53 return; 54 } 55 56 mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup")); 57 if (mBmgr == null) { 58 System.err.println(BMGR_NOT_RUNNING_ERR); 59 return; 60 } 61 62 mArgs = args; 63 String op = args[0]; 64 mNextArg = 1; 65 66 if ("enabled".equals(op)) { 67 doEnabled(); 68 return; 69 } 70 71 if ("enable".equals(op)) { 72 doEnable(); 73 return; 74 } 75 76 if ("run".equals(op)) { 77 doRun(); 78 return; 79 } 80 81 if ("backup".equals(op)) { 82 doBackup(); 83 return; 84 } 85 86 if ("list".equals(op)) { 87 doList(); 88 return; 89 } 90 91 if ("restore".equals(op)) { 92 doRestore(); 93 return; 94 } 95 96 if ("transport".equals(op)) { 97 doTransport(); 98 return; 99 } 100 101 if ("wipe".equals(op)) { 102 doWipe(); 103 return; 104 } 105 106 if ("fullbackup".equals(op)) { 107 doFullTransportBackup(); 108 return; 109 } 110 111 System.err.println("Unknown command"); 112 showUsage(); 113 } 114 115 private String enableToString(boolean enabled) { 116 return enabled ? "enabled" : "disabled"; 117 } 118 119 private void doEnabled() { 120 try { 121 boolean isEnabled = mBmgr.isBackupEnabled(); 122 System.out.println("Backup Manager currently " 123 + enableToString(isEnabled)); 124 } catch (RemoteException e) { 125 System.err.println(e.toString()); 126 System.err.println(BMGR_NOT_RUNNING_ERR); 127 } 128 } 129 130 private void doEnable() { 131 String arg = nextArg(); 132 if (arg == null) { 133 showUsage(); 134 return; 135 } 136 137 try { 138 boolean enable = Boolean.parseBoolean(arg); 139 mBmgr.setBackupEnabled(enable); 140 System.out.println("Backup Manager now " + enableToString(enable)); 141 } catch (NumberFormatException e) { 142 showUsage(); 143 return; 144 } catch (RemoteException e) { 145 System.err.println(e.toString()); 146 System.err.println(BMGR_NOT_RUNNING_ERR); 147 } 148 } 149 150 private void doRun() { 151 try { 152 mBmgr.backupNow(); 153 } catch (RemoteException e) { 154 System.err.println(e.toString()); 155 System.err.println(BMGR_NOT_RUNNING_ERR); 156 } 157 } 158 159 private void doBackup() { 160 String pkg = nextArg(); 161 if (pkg == null) { 162 showUsage(); 163 return; 164 } 165 166 try { 167 mBmgr.dataChanged(pkg); 168 } catch (RemoteException e) { 169 System.err.println(e.toString()); 170 System.err.println(BMGR_NOT_RUNNING_ERR); 171 } 172 } 173 174 private void doFullTransportBackup() { 175 System.out.println("Performing full transport backup"); 176 177 String pkg; 178 ArrayList<String> allPkgs = new ArrayList<String>(); 179 while ((pkg = nextArg()) != null) { 180 allPkgs.add(pkg); 181 } 182 if (allPkgs.size() > 0) { 183 try { 184 mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()])); 185 } catch (RemoteException e) { 186 System.err.println(e.toString()); 187 System.err.println(BMGR_NOT_RUNNING_ERR); 188 } 189 } 190 } 191 192 private void doTransport() { 193 try { 194 String which = nextArg(); 195 if (which == null) { 196 showUsage(); 197 return; 198 } 199 200 String old = mBmgr.selectBackupTransport(which); 201 if (old == null) { 202 System.out.println("Unknown transport '" + which 203 + "' specified; no changes made."); 204 } else { 205 System.out.println("Selected transport " + which + " (formerly " + old + ")"); 206 } 207 } catch (RemoteException e) { 208 System.err.println(e.toString()); 209 System.err.println(BMGR_NOT_RUNNING_ERR); 210 } 211 } 212 213 private void doWipe() { 214 String transport = nextArg(); 215 if (transport == null) { 216 showUsage(); 217 return; 218 } 219 220 String pkg = nextArg(); 221 if (pkg == null) { 222 showUsage(); 223 return; 224 } 225 226 try { 227 mBmgr.clearBackupData(transport, pkg); 228 System.out.println("Wiped backup data for " + pkg + " on " + transport); 229 } catch (RemoteException e) { 230 System.err.println(e.toString()); 231 System.err.println(BMGR_NOT_RUNNING_ERR); 232 } 233 } 234 235 private void doList() { 236 String arg = nextArg(); // sets, transports, packages set# 237 if ("transports".equals(arg)) { 238 doListTransports(); 239 return; 240 } 241 242 // The rest of the 'list' options work with a restore session on the current transport 243 try { 244 mRestore = mBmgr.beginRestoreSession(null, null); 245 if (mRestore == null) { 246 System.err.println(BMGR_NOT_RUNNING_ERR); 247 return; 248 } 249 250 if ("sets".equals(arg)) { 251 doListRestoreSets(); 252 } else if ("transports".equals(arg)) { 253 doListTransports(); 254 } 255 256 mRestore.endRestoreSession(); 257 } catch (RemoteException e) { 258 System.err.println(e.toString()); 259 System.err.println(BMGR_NOT_RUNNING_ERR); 260 } 261 } 262 263 private void doListTransports() { 264 try { 265 String current = mBmgr.getCurrentTransport(); 266 String[] transports = mBmgr.listAllTransports(); 267 if (transports == null || transports.length == 0) { 268 System.out.println("No transports available."); 269 return; 270 } 271 272 for (String t : transports) { 273 String pad = (t.equals(current)) ? " * " : " "; 274 System.out.println(pad + t); 275 } 276 } catch (RemoteException e) { 277 System.err.println(e.toString()); 278 System.err.println(BMGR_NOT_RUNNING_ERR); 279 } 280 } 281 282 private void doListRestoreSets() { 283 try { 284 RestoreObserver observer = new RestoreObserver(); 285 int err = mRestore.getAvailableRestoreSets(observer); 286 if (err != 0) { 287 System.out.println("Unable to request restore sets"); 288 } else { 289 observer.waitForCompletion(); 290 printRestoreSets(observer.sets); 291 } 292 } catch (RemoteException e) { 293 System.err.println(e.toString()); 294 System.err.println(TRANSPORT_NOT_RUNNING_ERR); 295 } 296 } 297 298 private void printRestoreSets(RestoreSet[] sets) { 299 if (sets == null || sets.length == 0) { 300 System.out.println("No restore sets"); 301 return; 302 } 303 for (RestoreSet s : sets) { 304 System.out.println(" " + Long.toHexString(s.token) + " : " + s.name); 305 } 306 } 307 308 class RestoreObserver extends IRestoreObserver.Stub { 309 boolean done; 310 RestoreSet[] sets = null; 311 312 public void restoreSetsAvailable(RestoreSet[] result) { 313 synchronized (this) { 314 sets = result; 315 done = true; 316 this.notify(); 317 } 318 } 319 320 public void restoreStarting(int numPackages) { 321 System.out.println("restoreStarting: " + numPackages + " packages"); 322 } 323 324 public void onUpdate(int nowBeingRestored, String currentPackage) { 325 System.out.println("onUpdate: " + nowBeingRestored + " = " + currentPackage); 326 } 327 328 public void restoreFinished(int error) { 329 System.out.println("restoreFinished: " + error); 330 synchronized (this) { 331 done = true; 332 this.notify(); 333 } 334 } 335 336 public void waitForCompletion() { 337 // The restoreFinished() callback will throw the 'done' flag; we 338 // just sit and wait on that notification. 339 synchronized (this) { 340 while (!this.done) { 341 try { 342 this.wait(); 343 } catch (InterruptedException ex) { 344 } 345 } 346 } 347 } 348 } 349 350 private void doRestore() { 351 String arg = nextArg(); 352 if (arg == null) { 353 showUsage(); 354 return; 355 } 356 357 if (arg.indexOf('.') >= 0 || arg.equals("android")) { 358 // it's a package name 359 doRestorePackage(arg); 360 } else { 361 try { 362 long token = Long.parseLong(arg, 16); 363 HashSet<String> filter = null; 364 while ((arg = nextArg()) != null) { 365 if (filter == null) filter = new HashSet<String>(); 366 filter.add(arg); 367 } 368 369 doRestoreAll(token, filter); 370 } catch (NumberFormatException e) { 371 showUsage(); 372 return; 373 } 374 } 375 376 System.out.println("done"); 377 } 378 379 private void doRestorePackage(String pkg) { 380 try { 381 mRestore = mBmgr.beginRestoreSession(pkg, null); 382 if (mRestore == null) { 383 System.err.println(BMGR_NOT_RUNNING_ERR); 384 return; 385 } 386 387 RestoreObserver observer = new RestoreObserver(); 388 int err = mRestore.restorePackage(pkg, observer); 389 if (err == 0) { 390 // Off and running -- wait for the restore to complete 391 observer.waitForCompletion(); 392 } else { 393 System.err.println("Unable to restore package " + pkg); 394 } 395 396 // And finally shut down the session 397 mRestore.endRestoreSession(); 398 } catch (RemoteException e) { 399 System.err.println(e.toString()); 400 System.err.println(BMGR_NOT_RUNNING_ERR); 401 } 402 } 403 404 private void doRestoreAll(long token, HashSet<String> filter) { 405 RestoreObserver observer = new RestoreObserver(); 406 407 try { 408 boolean didRestore = false; 409 mRestore = mBmgr.beginRestoreSession(null, null); 410 if (mRestore == null) { 411 System.err.println(BMGR_NOT_RUNNING_ERR); 412 return; 413 } 414 RestoreSet[] sets = null; 415 int err = mRestore.getAvailableRestoreSets(observer); 416 if (err == 0) { 417 observer.waitForCompletion(); 418 sets = observer.sets; 419 if (sets != null) { 420 for (RestoreSet s : sets) { 421 if (s.token == token) { 422 System.out.println("Scheduling restore: " + s.name); 423 if (filter == null) { 424 didRestore = (mRestore.restoreAll(token, observer) == 0); 425 } else { 426 String[] names = new String[filter.size()]; 427 filter.toArray(names); 428 didRestore = (mRestore.restoreSome(token, observer, names) == 0); 429 } 430 break; 431 } 432 } 433 } 434 } 435 if (!didRestore) { 436 if (sets == null || sets.length == 0) { 437 System.out.println("No available restore sets; no restore performed"); 438 } else { 439 System.out.println("No matching restore set token. Available sets:"); 440 printRestoreSets(sets); 441 } 442 } 443 444 // if we kicked off a restore successfully, we have to wait for it 445 // to complete before we can shut down the restore session safely 446 if (didRestore) { 447 observer.waitForCompletion(); 448 } 449 450 // once the restore has finished, close down the session and we're done 451 mRestore.endRestoreSession(); 452 } catch (RemoteException e) { 453 System.err.println(e.toString()); 454 System.err.println(BMGR_NOT_RUNNING_ERR); 455 } 456 } 457 458 private String nextArg() { 459 if (mNextArg >= mArgs.length) { 460 return null; 461 } 462 String arg = mArgs[mNextArg]; 463 mNextArg++; 464 return arg; 465 } 466 467 private static void showUsage() { 468 System.err.println("usage: bmgr [backup|restore|list|transport|run]"); 469 System.err.println(" bmgr backup PACKAGE"); 470 System.err.println(" bmgr enable BOOL"); 471 System.err.println(" bmgr enabled"); 472 System.err.println(" bmgr list transports"); 473 System.err.println(" bmgr list sets"); 474 System.err.println(" bmgr transport WHICH"); 475 System.err.println(" bmgr restore TOKEN"); 476 System.err.println(" bmgr restore TOKEN PACKAGE..."); 477 System.err.println(" bmgr restore PACKAGE"); 478 System.err.println(" bmgr run"); 479 System.err.println(" bmgr wipe TRANSPORT PACKAGE"); 480 System.err.println(" bmgr fullbackup PACKAGE..."); 481 System.err.println(""); 482 System.err.println("The 'backup' command schedules a backup pass for the named package."); 483 System.err.println("Note that the backup pass will effectively be a no-op if the package"); 484 System.err.println("does not actually have changed data to store."); 485 System.err.println(""); 486 System.err.println("The 'enable' command enables or disables the entire backup mechanism."); 487 System.err.println("If the argument is 'true' it will be enabled, otherwise it will be"); 488 System.err.println("disabled. When disabled, neither backup or restore operations will"); 489 System.err.println("be performed."); 490 System.err.println(""); 491 System.err.println("The 'enabled' command reports the current enabled/disabled state of"); 492 System.err.println("the backup mechanism."); 493 System.err.println(""); 494 System.err.println("The 'list transports' command reports the names of the backup transports"); 495 System.err.println("currently available on the device. These names can be passed as arguments"); 496 System.err.println("to the 'transport' and 'wipe' commands. The currently active transport"); 497 System.err.println("is indicated with a '*' character."); 498 System.err.println(""); 499 System.err.println("The 'list sets' command reports the token and name of each restore set"); 500 System.err.println("available to the device via the currently active transport."); 501 System.err.println(""); 502 System.err.println("The 'transport' command designates the named transport as the currently"); 503 System.err.println("active one. This setting is persistent across reboots."); 504 System.err.println(""); 505 System.err.println("The 'restore' command when given just a restore token initiates a full-system"); 506 System.err.println("restore operation from the currently active transport. It will deliver"); 507 System.err.println("the restore set designated by the TOKEN argument to each application"); 508 System.err.println("that had contributed data to that restore set."); 509 System.err.println(""); 510 System.err.println("The 'restore' command when given a token and one or more package names"); 511 System.err.println("initiates a restore operation of just those given packages from the restore"); 512 System.err.println("set designated by the TOKEN argument. It is effectively the same as the"); 513 System.err.println("'restore' operation supplying only a token, but applies a filter to the"); 514 System.err.println("set of applications to be restored."); 515 System.err.println(""); 516 System.err.println("The 'restore' command when given just a package name intiates a restore of"); 517 System.err.println("just that one package according to the restore set selection algorithm"); 518 System.err.println("used by the RestoreSession.restorePackage() method."); 519 System.err.println(""); 520 System.err.println("The 'run' command causes any scheduled backup operation to be initiated"); 521 System.err.println("immediately, without the usual waiting period for batching together"); 522 System.err.println("data changes."); 523 System.err.println(""); 524 System.err.println("The 'wipe' command causes all backed-up data for the given package to be"); 525 System.err.println("erased from the given transport's storage. The next backup operation"); 526 System.err.println("that the given application performs will rewrite its entire data set."); 527 System.err.println("Transport names to use here are those reported by 'list transports'."); 528 System.err.println(""); 529 System.err.println("The 'fullbackup' command induces a full-data stream backup for one or more"); 530 System.err.println("packages. The data is sent via the currently active transport."); 531 } 532} 533