blob: 9728e384d9a2cfac0ad7555c3f9ec412fe6d16d2 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.commands.bmgr;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
import android.os.RemoteException;
import android.os.ServiceManager;
import java.util.ArrayList;
import java.util.HashSet;
public final class Bmgr {
IBackupManager mBmgr;
IRestoreSession mRestore;
static final String BMGR_NOT_RUNNING_ERR =
"Error: Could not access the Backup Manager. Is the system running?";
static final String TRANSPORT_NOT_RUNNING_ERR =
"Error: Could not access the backup transport. Is the system running?";
private String[] mArgs;
private int mNextArg;
public static void main(String[] args) {
try {
new Bmgr().run(args);
} catch (Exception e) {
System.err.println("Exception caught:");
e.printStackTrace();
}
}
public void run(String[] args) {
if (args.length < 1) {
showUsage();
return;
}
mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
if (mBmgr == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
}
mArgs = args;
String op = args[0];
mNextArg = 1;
if ("enabled".equals(op)) {
doEnabled();
return;
}
if ("enable".equals(op)) {
doEnable();
return;
}
if ("run".equals(op)) {
doRun();
return;
}
if ("backup".equals(op)) {
doBackup();
return;
}
if ("list".equals(op)) {
doList();
return;
}
if ("restore".equals(op)) {
doRestore();
return;
}
if ("transport".equals(op)) {
doTransport();
return;
}
if ("wipe".equals(op)) {
doWipe();
return;
}
if ("fullbackup".equals(op)) {
doFullTransportBackup();
return;
}
System.err.println("Unknown command");
showUsage();
}
private String enableToString(boolean enabled) {
return enabled ? "enabled" : "disabled";
}
private void doEnabled() {
try {
boolean isEnabled = mBmgr.isBackupEnabled();
System.out.println("Backup Manager currently "
+ enableToString(isEnabled));
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doEnable() {
String arg = nextArg();
if (arg == null) {
showUsage();
return;
}
try {
boolean enable = Boolean.parseBoolean(arg);
mBmgr.setBackupEnabled(enable);
System.out.println("Backup Manager now " + enableToString(enable));
} catch (NumberFormatException e) {
showUsage();
return;
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doRun() {
try {
mBmgr.backupNow();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doBackup() {
String pkg = nextArg();
if (pkg == null) {
showUsage();
return;
}
try {
mBmgr.dataChanged(pkg);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doFullTransportBackup() {
System.out.println("Performing full transport backup");
String pkg;
ArrayList<String> allPkgs = new ArrayList<String>();
while ((pkg = nextArg()) != null) {
allPkgs.add(pkg);
}
if (allPkgs.size() > 0) {
try {
mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
}
private void doTransport() {
try {
String which = nextArg();
if (which == null) {
showUsage();
return;
}
String old = mBmgr.selectBackupTransport(which);
if (old == null) {
System.out.println("Unknown transport '" + which
+ "' specified; no changes made.");
} else {
System.out.println("Selected transport " + which + " (formerly " + old + ")");
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doWipe() {
String transport = nextArg();
if (transport == null) {
showUsage();
return;
}
String pkg = nextArg();
if (pkg == null) {
showUsage();
return;
}
try {
mBmgr.clearBackupData(transport, pkg);
System.out.println("Wiped backup data for " + pkg + " on " + transport);
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doList() {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
doListTransports();
return;
}
// The rest of the 'list' options work with a restore session on the current transport
try {
mRestore = mBmgr.beginRestoreSession(null, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
}
if ("sets".equals(arg)) {
doListRestoreSets();
} else if ("transports".equals(arg)) {
doListTransports();
}
mRestore.endRestoreSession();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doListTransports() {
try {
String current = mBmgr.getCurrentTransport();
String[] transports = mBmgr.listAllTransports();
if (transports == null || transports.length == 0) {
System.out.println("No transports available.");
return;
}
for (String t : transports) {
String pad = (t.equals(current)) ? " * " : " ";
System.out.println(pad + t);
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doListRestoreSets() {
try {
RestoreObserver observer = new RestoreObserver();
int err = mRestore.getAvailableRestoreSets(observer);
if (err != 0) {
System.out.println("Unable to request restore sets");
} else {
observer.waitForCompletion();
printRestoreSets(observer.sets);
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(TRANSPORT_NOT_RUNNING_ERR);
}
}
private void printRestoreSets(RestoreSet[] sets) {
if (sets == null || sets.length == 0) {
System.out.println("No restore sets");
return;
}
for (RestoreSet s : sets) {
System.out.println(" " + Long.toHexString(s.token) + " : " + s.name);
}
}
class RestoreObserver extends IRestoreObserver.Stub {
boolean done;
RestoreSet[] sets = null;
public void restoreSetsAvailable(RestoreSet[] result) {
synchronized (this) {
sets = result;
done = true;
this.notify();
}
}
public void restoreStarting(int numPackages) {
System.out.println("restoreStarting: " + numPackages + " packages");
}
public void onUpdate(int nowBeingRestored, String currentPackage) {
System.out.println("onUpdate: " + nowBeingRestored + " = " + currentPackage);
}
public void restoreFinished(int error) {
System.out.println("restoreFinished: " + error);
synchronized (this) {
done = true;
this.notify();
}
}
public void waitForCompletion() {
// The restoreFinished() callback will throw the 'done' flag; we
// just sit and wait on that notification.
synchronized (this) {
while (!this.done) {
try {
this.wait();
} catch (InterruptedException ex) {
}
}
}
}
}
private void doRestore() {
String arg = nextArg();
if (arg == null) {
showUsage();
return;
}
if (arg.indexOf('.') >= 0 || arg.equals("android")) {
// it's a package name
doRestorePackage(arg);
} else {
try {
long token = Long.parseLong(arg, 16);
HashSet<String> filter = null;
while ((arg = nextArg()) != null) {
if (filter == null) filter = new HashSet<String>();
filter.add(arg);
}
doRestoreAll(token, filter);
} catch (NumberFormatException e) {
showUsage();
return;
}
}
System.out.println("done");
}
private void doRestorePackage(String pkg) {
try {
mRestore = mBmgr.beginRestoreSession(pkg, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
}
RestoreObserver observer = new RestoreObserver();
int err = mRestore.restorePackage(pkg, observer);
if (err == 0) {
// Off and running -- wait for the restore to complete
observer.waitForCompletion();
} else {
System.err.println("Unable to restore package " + pkg);
}
// And finally shut down the session
mRestore.endRestoreSession();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private void doRestoreAll(long token, HashSet<String> filter) {
RestoreObserver observer = new RestoreObserver();
try {
boolean didRestore = false;
mRestore = mBmgr.beginRestoreSession(null, null);
if (mRestore == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
}
RestoreSet[] sets = null;
int err = mRestore.getAvailableRestoreSets(observer);
if (err == 0) {
observer.waitForCompletion();
sets = observer.sets;
if (sets != null) {
for (RestoreSet s : sets) {
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
if (filter == null) {
didRestore = (mRestore.restoreAll(token, observer) == 0);
} else {
String[] names = new String[filter.size()];
filter.toArray(names);
didRestore = (mRestore.restoreSome(token, observer, names) == 0);
}
break;
}
}
}
}
if (!didRestore) {
if (sets == null || sets.length == 0) {
System.out.println("No available restore sets; no restore performed");
} else {
System.out.println("No matching restore set token. Available sets:");
printRestoreSets(sets);
}
}
// if we kicked off a restore successfully, we have to wait for it
// to complete before we can shut down the restore session safely
if (didRestore) {
observer.waitForCompletion();
}
// once the restore has finished, close down the session and we're done
mRestore.endRestoreSession();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
}
String arg = mArgs[mNextArg];
mNextArg++;
return arg;
}
private static void showUsage() {
System.err.println("usage: bmgr [backup|restore|list|transport|run]");
System.err.println(" bmgr backup PACKAGE");
System.err.println(" bmgr enable BOOL");
System.err.println(" bmgr enabled");
System.err.println(" bmgr list transports");
System.err.println(" bmgr list sets");
System.err.println(" bmgr transport WHICH");
System.err.println(" bmgr restore TOKEN");
System.err.println(" bmgr restore TOKEN PACKAGE...");
System.err.println(" bmgr restore PACKAGE");
System.err.println(" bmgr run");
System.err.println(" bmgr wipe TRANSPORT PACKAGE");
System.err.println(" bmgr fullbackup PACKAGE...");
System.err.println("");
System.err.println("The 'backup' command schedules a backup pass for the named package.");
System.err.println("Note that the backup pass will effectively be a no-op if the package");
System.err.println("does not actually have changed data to store.");
System.err.println("");
System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
System.err.println("disabled. When disabled, neither backup or restore operations will");
System.err.println("be performed.");
System.err.println("");
System.err.println("The 'enabled' command reports the current enabled/disabled state of");
System.err.println("the backup mechanism.");
System.err.println("");
System.err.println("The 'list transports' command reports the names of the backup transports");
System.err.println("currently available on the device. These names can be passed as arguments");
System.err.println("to the 'transport' and 'wipe' commands. The currently active transport");
System.err.println("is indicated with a '*' character.");
System.err.println("");
System.err.println("The 'list sets' command reports the token and name of each restore set");
System.err.println("available to the device via the currently active transport.");
System.err.println("");
System.err.println("The 'transport' command designates the named transport as the currently");
System.err.println("active one. This setting is persistent across reboots.");
System.err.println("");
System.err.println("The 'restore' command when given just a restore token initiates a full-system");
System.err.println("restore operation from the currently active transport. It will deliver");
System.err.println("the restore set designated by the TOKEN argument to each application");
System.err.println("that had contributed data to that restore set.");
System.err.println("");
System.err.println("The 'restore' command when given a token and one or more package names");
System.err.println("initiates a restore operation of just those given packages from the restore");
System.err.println("set designated by the TOKEN argument. It is effectively the same as the");
System.err.println("'restore' operation supplying only a token, but applies a filter to the");
System.err.println("set of applications to be restored.");
System.err.println("");
System.err.println("The 'restore' command when given just a package name intiates a restore of");
System.err.println("just that one package according to the restore set selection algorithm");
System.err.println("used by the RestoreSession.restorePackage() method.");
System.err.println("");
System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
System.err.println("immediately, without the usual waiting period for batching together");
System.err.println("data changes.");
System.err.println("");
System.err.println("The 'wipe' command causes all backed-up data for the given package to be");
System.err.println("erased from the given transport's storage. The next backup operation");
System.err.println("that the given application performs will rewrite its entire data set.");
System.err.println("Transport names to use here are those reported by 'list transports'.");
System.err.println("");
System.err.println("The 'fullbackup' command induces a full-data stream backup for one or more");
System.err.println("packages. The data is sent via the currently active transport.");
}
}