blob: ed717c4914676fcaffe97a2d0120f996802d6a63 [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.annotation.IntDef;
import android.annotation.UserIdInt;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
import android.app.backup.RestoreSet;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
* Adb shell command for {@link android.app.backup.IBackupManager}.
*/
public class Bmgr {
public static final String TAG = "Bmgr";
private final IBackupManager mBmgr;
private IRestoreSession mRestore;
private static final String BMGR_NOT_RUNNING_ERR =
"Error: Could not access the Backup Manager. Is the system running?";
private static final String BMGR_NOT_ACTIVATED_FOR_USER =
"Error: Backup Manager is not activated for user ";
private static final String BMGR_ERR_NO_RESTORESESSION_FOR_USER =
"Error: Could not get restore session for user ";
private static final String TRANSPORT_NOT_RUNNING_ERR =
"Error: Could not access the backup transport. Is the system running?";
private static final String PM_NOT_RUNNING_ERR =
"Error: Could not access the Package Manager. Is the system running?";
private String[] mArgs;
private int mNextArg;
@VisibleForTesting
Bmgr(IBackupManager bmgr) {
mBmgr = bmgr;
}
Bmgr() {
mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
}
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;
}
mArgs = args;
mNextArg = 0;
int userId = parseUserId();
String op = nextArg();
Slog.v(TAG, "Running " + op + " for user:" + userId);
if (mBmgr == null) {
System.err.println(BMGR_NOT_RUNNING_ERR);
return;
}
if ("activate".equals(op)) {
doActivateService(userId);
return;
}
if ("activated".equals(op)) {
doActivated(userId);
return;
}
if (!isBackupActive(userId)) {
return;
}
if ("autorestore".equals(op)) {
doAutoRestore(userId);
return;
}
if ("enabled".equals(op)) {
doEnabled(userId);
return;
}
if ("enable".equals(op)) {
doEnable(userId);
return;
}
if ("run".equals(op)) {
doRun(userId);
return;
}
if ("backup".equals(op)) {
doBackup(userId);
return;
}
if ("init".equals(op)) {
doInit(userId);
return;
}
if ("list".equals(op)) {
doList(userId);
return;
}
if ("restore".equals(op)) {
doRestore(userId);
return;
}
if ("transport".equals(op)) {
doTransport(userId);
return;
}
if ("wipe".equals(op)) {
doWipe(userId);
return;
}
if ("fullbackup".equals(op)) {
doFullTransportBackup(userId);
return;
}
if ("backupnow".equals(op)) {
doBackupNow(userId);
return;
}
if ("cancel".equals(op)) {
doCancel(userId);
return;
}
if ("whitelist".equals(op)) {
doPrintWhitelist();
return;
}
System.err.println("Unknown command");
showUsage();
}
private void handleRemoteException(RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
private boolean isBackupActive(@UserIdInt int userId) {
try {
if (!mBmgr.isBackupServiceActive(userId)) {
System.err.println(BMGR_NOT_ACTIVATED_FOR_USER + userId);
return false;
}
} catch (RemoteException e) {
handleRemoteException(e);
return false;
}
return true;
}
private void doAutoRestore(int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
return;
}
try {
boolean enable = Boolean.parseBoolean(arg);
mBmgr.setAutoRestore(enable);
System.out.println(
"Auto restore is now "
+ (enable ? "enabled" : "disabled")
+ " for user "
+ userId);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private String activatedToString(boolean activated) {
return activated ? "activated" : "deactivated";
}
private void doActivated(@UserIdInt int userId) {
try {
System.out.println("Backup Manager currently "
+ activatedToString(mBmgr.isBackupServiceActive(userId)));
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private String enableToString(boolean enabled) {
return enabled ? "enabled" : "disabled";
}
private void doEnabled(@UserIdInt int userId) {
try {
boolean isEnabled = mBmgr.isBackupEnabledForUser(userId);
System.out.println("Backup Manager currently "
+ enableToString(isEnabled));
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doEnable(@UserIdInt int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
return;
}
try {
boolean enable = Boolean.parseBoolean(arg);
mBmgr.setBackupEnabledForUser(userId, enable);
System.out.println("Backup Manager now " + enableToString(enable));
} catch (NumberFormatException e) {
showUsage();
return;
} catch (RemoteException e) {
handleRemoteException(e);
}
}
void doRun(@UserIdInt int userId) {
try {
mBmgr.backupNowForUser(userId);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doBackup(@UserIdInt int userId) {
String pkg = nextArg();
if (pkg == null) {
showUsage();
return;
}
try {
mBmgr.dataChangedForUser(userId, pkg);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doFullTransportBackup(@UserIdInt int userId) {
System.out.println("Performing full transport backup");
String pkg;
ArraySet<String> allPkgs = new ArraySet<String>();
while ((pkg = nextArg()) != null) {
allPkgs.add(pkg);
}
if (allPkgs.size() > 0) {
try {
mBmgr.fullTransportBackupForUser(
userId, allPkgs.toArray(new String[allPkgs.size()]));
} catch (RemoteException e) {
handleRemoteException(e);
}
}
}
// IBackupObserver generically usable for any backup/init operation
private static abstract class Observer extends IBackupObserver.Stub {
private final Object trigger = new Object();
@GuardedBy("trigger")
private volatile boolean done = false;
@Override
public void onUpdate(String currentPackage, BackupProgress backupProgress) {
}
@Override
public void onResult(String currentPackage, int status) {
}
@Override
public void backupFinished(int status) {
synchronized (trigger) {
done = true;
trigger.notify();
}
}
public boolean done() {
return this.done;
}
// Wait forever
public void waitForCompletion() {
waitForCompletion(0);
}
// Wait for a given time and then give up
public void waitForCompletion(long timeout) {
// The backupFinished() callback will throw the 'done' flag; we
// just sit and wait on that notification.
final long targetTime = SystemClock.elapsedRealtime() + timeout;
synchronized (trigger) {
// Wait until either we're done, or we've reached a stated positive timeout
while (!done && (timeout <= 0 || SystemClock.elapsedRealtime() < targetTime)) {
try {
trigger.wait(1000L);
} catch (InterruptedException ex) {
}
}
}
}
}
private static class BackupObserver extends Observer {
@Override
public void onUpdate(String currentPackage, BackupProgress backupProgress) {
super.onUpdate(currentPackage, backupProgress);
System.out.println(
"Package " + currentPackage + " with progress: " + backupProgress.bytesTransferred
+ "/" + backupProgress.bytesExpected);
}
@Override
public void onResult(String currentPackage, int status) {
super.onResult(currentPackage, status);
System.out.println("Package " + currentPackage + " with result: "
+ convertBackupStatusToString(status));
}
@Override
public void backupFinished(int status) {
super.backupFinished(status);
System.out.println("Backup finished with result: "
+ convertBackupStatusToString(status));
if (status == BackupManager.ERROR_BACKUP_CANCELLED) {
System.out.println("Backups can be cancelled if a backup is already running, check "
+ "backup dumpsys");
}
}
}
private static String convertBackupStatusToString(int errorCode) {
switch (errorCode) {
case BackupManager.SUCCESS:
return "Success";
case BackupManager.ERROR_BACKUP_NOT_ALLOWED:
return "Backup is not allowed";
case BackupManager.ERROR_PACKAGE_NOT_FOUND:
return "Package not found";
case BackupManager.ERROR_TRANSPORT_ABORTED:
return "Transport error";
case BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED:
return "Transport rejected package because it wasn't able to process it"
+ " at the time";
case BackupManager.ERROR_AGENT_FAILURE:
return "Agent error";
case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
return "Size quota exceeded";
case BackupManager.ERROR_BACKUP_CANCELLED:
return "Backup cancelled";
default:
return "Unknown error";
}
}
private void backupNowAllPackages(@UserIdInt int userId, boolean nonIncrementalBackup,
@Monitor int monitorState) {
IPackageManager mPm =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
return;
}
List<PackageInfo> installedPackages = null;
try {
installedPackages = mPm.getInstalledPackages(0, userId).getList();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
}
if (installedPackages != null) {
String[] packages =
installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
String[] filteredPackages = {};
try {
filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages);
} catch (RemoteException e) {
handleRemoteException(e);
}
backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup,
monitorState);
}
}
private void backupNowPackages(
@UserIdInt int userId,
List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) {
int flags = 0;
if (nonIncrementalBackup) {
flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
}
try {
BackupObserver observer = new BackupObserver();
BackupMonitor monitor =
(monitorState != Monitor.OFF)
? new BackupMonitor(monitorState == Monitor.VERBOSE)
: null;
int err = mBmgr.requestBackupForUser(
userId,
packages.toArray(new String[packages.size()]),
observer,
monitor,
flags);
if (err == 0) {
// Off and running -- wait for the backup to complete
observer.waitForCompletion();
} else {
System.err.println("Unable to run backup");
}
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doBackupNow(@UserIdInt int userId) {
String pkg;
boolean backupAll = false;
boolean nonIncrementalBackup = false;
@Monitor int monitor = Monitor.OFF;
ArrayList<String> allPkgs = new ArrayList<String>();
while ((pkg = nextArg()) != null) {
if (pkg.equals("--all")) {
backupAll = true;
} else if (pkg.equals("--non-incremental")) {
nonIncrementalBackup = true;
} else if (pkg.equals("--incremental")) {
nonIncrementalBackup = false;
} else if (pkg.equals("--monitor")) {
monitor = Monitor.NORMAL;
} else if (pkg.equals("--monitor-verbose")) {
monitor = Monitor.VERBOSE;
} else {
if (!allPkgs.contains(pkg)) {
allPkgs.add(pkg);
}
}
}
if (backupAll) {
if (allPkgs.size() == 0) {
System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
"incremental backup for all packages.");
backupNowAllPackages(userId, nonIncrementalBackup, monitor);
} else {
System.err.println("Provide only '--all' flag or list of packages.");
}
} else if (allPkgs.size() > 0) {
System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
"incremental backup for " + allPkgs.size() +" requested packages.");
backupNowPackages(userId, allPkgs, nonIncrementalBackup, monitor);
} else {
System.err.println("Provide '--all' flag or list of packages.");
}
}
private void doCancel(@UserIdInt int userId) {
String arg = nextArg();
if ("backups".equals(arg)) {
try {
mBmgr.cancelBackupsForUser(userId);
} catch (RemoteException e) {
handleRemoteException(e);
}
return;
}
System.err.println("Unknown command.");
}
private void doTransport(@UserIdInt int userId) {
try {
String which = nextArg();
if (which == null) {
showUsage();
return;
}
if ("-c".equals(which)) {
doTransportByComponent(userId);
return;
}
String old = mBmgr.selectBackupTransportForUser(userId, 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) {
handleRemoteException(e);
}
}
private void doTransportByComponent(@UserIdInt int userId) {
String which = nextArg();
if (which == null) {
showUsage();
return;
}
final CountDownLatch latch = new CountDownLatch(1);
try {
mBmgr.selectBackupTransportAsyncForUser(
userId,
ComponentName.unflattenFromString(which),
new ISelectBackupTransportCallback.Stub() {
@Override
public void onSuccess(String transportName) {
System.out.println("Success. Selected transport: " + transportName);
latch.countDown();
}
@Override
public void onFailure(int reason) {
System.err.println("Failure. error=" + reason);
latch.countDown();
}
});
} catch (RemoteException e) {
handleRemoteException(e);
return;
}
try {
latch.await();
} catch (InterruptedException e) {
System.err.println("Operation interrupted.");
}
}
private void doWipe(@UserIdInt int userId) {
String transport = nextArg();
if (transport == null) {
showUsage();
return;
}
String pkg = nextArg();
if (pkg == null) {
showUsage();
return;
}
try {
mBmgr.clearBackupDataForUser(userId, transport, pkg);
System.out.println("Wiped backup data for " + pkg + " on " + transport);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
class InitObserver extends Observer {
public int result = BackupTransport.TRANSPORT_ERROR;
@Override
public void backupFinished(int status) {
super.backupFinished(status);
result = status;
}
}
private void doInit(@UserIdInt int userId) {
ArraySet<String> transports = new ArraySet<>();
String transport;
while ((transport = nextArg()) != null) {
transports.add(transport);
}
if (transports.size() == 0) {
showUsage();
return;
}
InitObserver observer = new InitObserver();
try {
System.out.println("Initializing transports: " + transports);
mBmgr.initializeTransportsForUser(
userId, transports.toArray(new String[transports.size()]), observer);
observer.waitForCompletion(30*1000L);
System.out.println("Initialization result: " + observer.result);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doList(@UserIdInt int userId) {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
doListTransports(userId);
return;
}
// The rest of the 'list' options work with a restore session on the current transport
try {
mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
return;
}
if ("sets".equals(arg)) {
doListRestoreSets();
}
mRestore.endRestoreSession();
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doListTransports(@UserIdInt int userId) {
String arg = nextArg();
try {
if ("-c".equals(arg)) {
for (ComponentName transport : mBmgr.listAllTransportComponentsForUser(userId)) {
System.out.println(transport.flattenToShortString());
}
return;
}
String current = mBmgr.getCurrentTransportForUser(userId);
String[] transports = mBmgr.listAllTransportsForUser(userId);
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) {
handleRemoteException(e);
}
}
private void doListRestoreSets() {
try {
RestoreObserver observer = new RestoreObserver();
// TODO implement monitor here
int err = mRestore.getAvailableRestoreSets(observer, null);
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();
}
}
/**
* Wait until either {@link #restoreFinished} or {@link #restoreStarting} is called.
* Once one is called, it clears the internal flag again, so that the same observer intance
* can be reused for a next operation.
*/
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) {
}
}
done = false;
}
}
}
private void doRestore(@UserIdInt int userId) {
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(userId, token, filter);
} catch (NumberFormatException e) {
showUsage();
return;
}
}
}
private void doRestorePackage(String pkg) {
System.err.println("The syntax 'restore <package>' is no longer supported, please use ");
System.err.println("'restore <token> <package>'.");
}
private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
RestoreObserver observer = new RestoreObserver();
try {
boolean didRestore = false;
mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
return;
}
RestoreSet[] sets = null;
// TODO implement monitor here
int err = mRestore.getAvailableRestoreSets(observer, null);
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, null) == 0);
} else {
String[] names = new String[filter.size()];
filter.toArray(names);
didRestore = (mRestore.restorePackages(token, observer, names,
null) == 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();
System.out.println("done");
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doPrintWhitelist() {
try {
final String[] whitelist = mBmgr.getTransportWhitelist();
if (whitelist != null) {
for (String transport : whitelist) {
System.out.println(transport);
}
}
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private void doActivateService(int userId) {
String arg = nextArg();
if (arg == null) {
showUsage();
return;
}
try {
boolean activate = Boolean.parseBoolean(arg);
mBmgr.setBackupServiceActive(userId, activate);
System.out.println(
"Backup service now "
+ (activate ? "activated" : "deactivated")
+ " for user "
+ userId);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
}
String arg = mArgs[mNextArg];
mNextArg++;
return arg;
}
private int parseUserId() {
String arg = nextArg();
if ("--user".equals(arg)) {
return UserHandle.parseUserArg(nextArg());
} else {
mNextArg--;
return UserHandle.USER_SYSTEM;
}
}
private static void showUsage() {
System.err.println("usage: bmgr [--user <userId>] [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 [-c]");
System.err.println(" bmgr list sets");
System.err.println(" bmgr transport WHICH|-c WHICH_COMPONENT");
System.err.println(" bmgr restore TOKEN");
System.err.println(" bmgr restore TOKEN PACKAGE...");
System.err.println(" bmgr run");
System.err.println(" bmgr wipe TRANSPORT PACKAGE");
System.err.println(" bmgr fullbackup PACKAGE...");
System.err.println(" bmgr backupnow [--monitor|--monitor-verbose] --all|PACKAGE...");
System.err.println(" bmgr cancel backups");
System.err.println(" bmgr init TRANSPORT...");
System.err.println(" bmgr activate BOOL");
System.err.println(" bmgr activated");
System.err.println(" bmgr autorestore BOOL");
System.err.println("");
System.err.println("The '--user' option specifies the user on which the operation is run.");
System.err.println("It must be the first argument before the operation.");
System.err.println("The default value is 0 which is the system user.");
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("BackupManager is currently bound to. 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. If -c flag is used, all available");
System.err.println("transport components on the device are listed. These can be used with");
System.err.println("the component variant of 'transport' command.");
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. If -c flag is");
System.err.println("specified, the following string is treated as a component name.");
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 '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.");
System.err.println("");
System.err.println("The 'backupnow' command runs an immediate backup for one or more packages.");
System.err.println(" --all flag runs backup for all eligible packages.");
System.err.println(" --monitor flag prints monitor events.");
System.err.println(" --monitor-verbose flag prints monitor events with all keys.");
System.err.println("For each package it will run key/value or full data backup ");
System.err.println("depending on the package's manifest declarations.");
System.err.println("The data is sent via the currently active transport.");
System.err.println("");
System.err.println("The 'cancel backups' command cancels all running backups.");
System.err.println("");
System.err.println("The 'init' command initializes the given transports, wiping all data");
System.err.println("from their backing data stores.");
System.err.println("");
System.err.println("The 'activate' command activates or deactivates the backup service.");
System.err.println("If the argument is 'true' it will be activated, otherwise it will be");
System.err.println("deactivated. When deactivated, the service will not be running and no");
System.err.println("operations can be performed until activation.");
System.err.println("");
System.err.println("The 'activated' command reports the current activated/deactivated");
System.err.println("state of the backup mechanism.");
System.err.println("");
System.err.println("The 'autorestore' command enables or disables automatic restore when");
System.err.println("a new package is installed.");
}
private static class BackupMonitor extends IBackupManagerMonitor.Stub {
private final boolean mVerbose;
private BackupMonitor(boolean verbose) {
mVerbose = verbose;
}
@Override
public void onEvent(Bundle event) throws RemoteException {
StringBuilder out = new StringBuilder();
int id = event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
int category = event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
out.append("=> Event{").append(eventCategoryToString(category));
out.append(" / ").append(eventIdToString(id));
String packageName = event.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME);
if (packageName != null) {
out.append(" : package = ").append(packageName);
if (event.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
long version =
event.getLong(
BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION);
out.append("(v").append(version).append(")");
}
}
if (mVerbose) {
Set<String> remainingKeys = new ArraySet<>(event.keySet());
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME);
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION);
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION);
if (!remainingKeys.isEmpty()) {
out.append(", other keys =");
for (String key : remainingKeys) {
out.append(" ").append(key);
}
}
}
out.append("}");
System.out.println(out.toString());
}
}
private static String eventCategoryToString(int eventCategory) {
switch (eventCategory) {
case BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT:
return "TRANSPORT";
case BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT:
return "AGENT";
case BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY:
return "BACKUP_MANAGER_POLICY";
default:
return "UNKNOWN_CATEGORY";
}
}
private static String eventIdToString(int eventId) {
switch (eventId) {
case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL:
return "FULL_BACKUP_CANCEL";
case BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY:
return "ILLEGAL_KEY";
case BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND:
return "NO_DATA_TO_SEND";
case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE:
return "PACKAGE_INELIGIBLE";
case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT:
return "PACKAGE_KEY_VALUE_PARTICIPANT";
case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED:
return "PACKAGE_STOPPED";
case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND:
return "PACKAGE_NOT_FOUND";
case BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED:
return "BACKUP_DISABLED";
case BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED:
return "DEVICE_NOT_PROVISIONED";
case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT:
return "PACKAGE_TRANSPORT_NOT_PRESENT";
case BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT:
return "ERROR_PREFLIGHT";
case BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT:
return "QUOTA_HIT_PREFLIGHT";
case BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP:
return "EXCEPTION_FULL_BACKUP";
case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL:
return "KEY_VALUE_BACKUP_CANCEL";
case BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE:
return "NO_RESTORE_METADATA_AVAILABLE";
case BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED:
return "NO_PM_METADATA_RECEIVED";
case BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA:
return "PM_AGENT_HAS_NO_METADATA";
case BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT:
return "LOST_TRANSPORT";
case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT:
return "PACKAGE_NOT_PRESENT";
case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER:
return "RESTORE_VERSION_HIGHER";
case BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT:
return "APP_HAS_NO_AGENT";
case BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH:
return "SIGNATURE_MISMATCH";
case BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT:
return "CANT_FIND_AGENT";
case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT:
return "KEY_VALUE_RESTORE_TIMEOUT";
case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION:
return "RESTORE_ANY_VERSION";
case BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH:
return "VERSIONS_MATCH";
case BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER:
return "VERSION_OF_BACKUP_OLDER";
case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH:
return "FULL_RESTORE_SIGNATURE_MISMATCH";
case BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT:
return "SYSTEM_APP_NO_AGENT";
case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE:
return "FULL_RESTORE_ALLOW_BACKUP_FALSE";
case BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED:
return "APK_NOT_INSTALLED";
case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK:
return "CANNOT_RESTORE_WITHOUT_APK";
case BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE:
return "MISSING_SIGNATURE";
case BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE:
return "EXPECTED_DIFFERENT_PACKAGE";
case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION:
return "UNKNOWN_VERSION";
case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT:
return "FULL_RESTORE_TIMEOUT";
case BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST:
return "CORRUPT_MANIFEST";
case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH:
return "WIDGET_METADATA_MISMATCH";
case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION:
return "WIDGET_UNKNOWN_VERSION";
case BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES:
return "NO_PACKAGES";
case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL:
return "TRANSPORT_IS_NULL";
default:
return "UNKNOWN_ID";
}
}
@IntDef({Monitor.OFF, Monitor.NORMAL, Monitor.VERBOSE})
@Retention(RetentionPolicy.SOURCE)
private @interface Monitor {
int OFF = 0;
int NORMAL = 1;
int VERBOSE = 2;
}
}