Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 924afe2b authored by Christopher Tate's avatar Christopher Tate
Browse files

Add 'bmgr' command to synchronously init transports

bmgr init TRANSPORT [...]

will run an init operation on each named transport, blocking
until the operations have all completed.

Bug 62253989
Test: manual

Change-Id: I7dbd94293738d5ecf195764f5b28905253819791
parent 101ba665
Loading
Loading
Loading
Loading
+93 −18
Original line number Original line Diff line number Diff line
@@ -17,8 +17,8 @@
package com.android.commands.bmgr;
package com.android.commands.bmgr;


import android.app.backup.BackupManager;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
import android.app.backup.BackupProgress;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupObserver;
import android.app.backup.IBackupObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreObserver;
@@ -30,8 +30,11 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.util.Log;
import android.util.ArraySet;

import com.android.internal.annotations.GuardedBy;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashSet;
@@ -97,6 +100,11 @@ public final class Bmgr {
            return;
            return;
        }
        }


        if ("init".equals(op)) {
            doInit();
            return;
        }

        if ("list".equals(op)) {
        if ("list".equals(op)) {
            doList();
            doList();
            return;
            return;
@@ -204,7 +212,7 @@ public final class Bmgr {
        System.out.println("Performing full transport backup");
        System.out.println("Performing full transport backup");


        String pkg;
        String pkg;
        ArrayList<String> allPkgs = new ArrayList<String>();
        ArraySet<String> allPkgs = new ArraySet<String>();
        while ((pkg = nextArg()) != null) {
        while ((pkg = nextArg()) != null) {
            allPkgs.add(pkg);
            allPkgs.add(pkg);
        }
        }
@@ -218,45 +226,77 @@ public final class Bmgr {
        }
        }
    }
    }


    class BackupObserver extends IBackupObserver.Stub {
    // IBackupObserver generically usable for any backup/init operation
        boolean done = false;
    abstract class Observer extends IBackupObserver.Stub {
        private final Object trigger = new Object();

        @GuardedBy("trigger")
        private volatile boolean done = false;


        @Override
        @Override
        public void onUpdate(String currentPackage, BackupProgress backupProgress) {
        public void onUpdate(String currentPackage, BackupProgress backupProgress) {
            System.out.println(
                "Package " + currentPackage + " with progress: " + backupProgress.bytesTransferred
                    + "/" + backupProgress.bytesExpected);
        }
        }


        @Override
        @Override
        public void onResult(String currentPackage, int status) {
        public void onResult(String currentPackage, int status) {
            System.out.println("Package " + currentPackage + " with result: "
                    + convertBackupStatusToString(status));
        }
        }


        @Override
        @Override
        public void backupFinished(int status) {
        public void backupFinished(int status) {
            System.out.println("Backup finished with result: "
            synchronized (trigger) {
                    + convertBackupStatusToString(status));
            synchronized (this) {
                done = true;
                done = true;
                this.notify();
                trigger.notify();
            }
            }
        }
        }


        public boolean done() {
            return this.done;
        }

        // Wait forever
        public void waitForCompletion() {
        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
            // The backupFinished() callback will throw the 'done' flag; we
            // just sit and wait on that notification.
            // just sit and wait on that notification.
            synchronized (this) {
            final long targetTime = SystemClock.elapsedRealtime() + timeout;
                while (!this.done) {
            synchronized (trigger) {
                // Wait until either we're done, or we've reached a stated positive timeout
                while (!done && (timeout <= 0 || SystemClock.elapsedRealtime() < targetTime)) {
                    try {
                    try {
                        this.wait();
                        trigger.wait(1000L);
                    } catch (InterruptedException ex) {
                    } catch (InterruptedException ex) {
                    }
                    }
                }
                }
            }
            }
        }
        }
    }

    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));
        }
    }
    }


    private static String convertBackupStatusToString(int errorCode) {
    private static String convertBackupStatusToString(int errorCode) {
@@ -349,9 +389,11 @@ public final class Bmgr {
            } else if (pkg.equals("--incremental")) {
            } else if (pkg.equals("--incremental")) {
                nonIncrementalBackup = false;
                nonIncrementalBackup = false;
            } else {
            } else {
                if (!allPkgs.contains(pkg)) {
                    allPkgs.add(pkg);
                    allPkgs.add(pkg);
                }
                }
            }
            }
        }
        if (backupAll) {
        if (backupAll) {
            if (allPkgs.size() == 0) {
            if (allPkgs.size() == 0) {
                System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
@@ -470,6 +512,39 @@ public final class Bmgr {
        }
        }
    }
    }


    class InitObserver extends Observer {
        public int result = BackupTransport.TRANSPORT_ERROR;

        @Override
        public void backupFinished(int status) {
            super.backupFinished(status);
            result = status;
        }
    }

    private void doInit() {
        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.initializeTransports(transports.toArray(new String[transports.size()]), observer);
            observer.waitForCompletion(30*1000L);
            System.out.println("Initialization result: " + observer.result);
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(BMGR_NOT_RUNNING_ERR);
        }
    }

    private void doList() {
    private void doList() {
        String arg = nextArg();     // sets, transports, packages set#
        String arg = nextArg();     // sets, transports, packages set#
        if ("transports".equals(arg)) {
        if ("transports".equals(arg)) {
+9 −0
Original line number Original line Diff line number Diff line
@@ -56,6 +56,15 @@ interface IBackupManager {
     */
     */
    void clearBackupData(String transportName, String packageName);
    void clearBackupData(String transportName, String packageName);


    /**
     * Run an initialize operation on the given transports.  This will wipe all data from
     * the backing data store and establish a clean starting point for all backup
     * operations.
     *
     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
     */
    void initializeTransports(in String[] transportNames, IBackupObserver observer);

    /**
    /**
     * Notifies the Backup Manager Service that an agent has become available.  This
     * Notifies the Backup Manager Service that an agent has become available.  This
     * method is only invoked by the Activity Manager.
     * method is only invoked by the Activity Manager.
+8 −6
Original line number Original line Diff line number Diff line
@@ -29,20 +29,22 @@ oneway interface IBackupObserver {
     * This method could be called several times for packages with full data backup.
     * This method could be called several times for packages with full data backup.
     * It will tell how much of backup data is already saved and how much is expected.
     * It will tell how much of backup data is already saved and how much is expected.
     *
     *
     * @param currentBackupPackage The name of the package that now being backuped.
     * @param currentBackupPackage The name of the package that now being backed up.
     * @param backupProgress Current progress of backup for the package.
     * @param backupProgress Current progress of backup for the package.
     */
     */
    void onUpdate(String currentPackage, in BackupProgress backupProgress);
    void onUpdate(String currentPackage, in BackupProgress backupProgress);


    /**
    /**
     * The backup of single package has completed.  This method will be called at most one time
     * Backup of one package or initialization of one transport has completed.  This
     * for each package and could be not called if backup is failed before and
     * method will be called at most one time for each package or transport, and might not
     * backupFinished() is called.
     * be not called if the operation fails before backupFinished(); for example, if the
     * requested package/transport does not exist.
     *
     *
     * @param currentBackupPackage The name of the package that was backuped.
     * @param target The name of the package that was backed up, or of the transport
     *                  that was initialized
     * @param status Zero on success; a nonzero error code if the backup operation failed.
     * @param status Zero on success; a nonzero error code if the backup operation failed.
     */
     */
    void onResult(String currentPackage, int status);
    void onResult(String target, int status);


    /**
    /**
     * The backup process has completed.  This method will always be called,
     * The backup process has completed.  This method will always be called,
+57 −24
Original line number Original line Diff line number Diff line
@@ -106,6 +106,7 @@ import android.provider.Settings;
import android.system.ErrnoException;
import android.system.ErrnoException;
import android.system.Os;
import android.system.Os;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.EventLog;
import android.util.Log;
import android.util.Log;
@@ -123,8 +124,8 @@ import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemService;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import com.android.server.power.BatterySaverPolicy.ServiceType;
import com.android.server.power.BatterySaverPolicy.ServiceType;
import libcore.io.IoUtils;
import libcore.io.IoUtils;
import java.io.BufferedInputStream;
import java.io.BufferedInputStream;
@@ -259,7 +260,6 @@ public class BackupManagerService implements BackupManagerServiceInterface {
    private static final int MSG_RUN_ADB_BACKUP = 2;
    private static final int MSG_RUN_ADB_BACKUP = 2;
    private static final int MSG_RUN_RESTORE = 3;
    private static final int MSG_RUN_RESTORE = 3;
    private static final int MSG_RUN_CLEAR = 4;
    private static final int MSG_RUN_CLEAR = 4;
    private static final int MSG_RUN_INITIALIZE = 5;
    private static final int MSG_RUN_GET_RESTORE_SETS = 6;
    private static final int MSG_RUN_GET_RESTORE_SETS = 6;
    private static final int MSG_RESTORE_SESSION_TIMEOUT = 8;
    private static final int MSG_RESTORE_SESSION_TIMEOUT = 8;
    private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
    private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
@@ -733,7 +733,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
    // Persistently track the need to do a full init
    // Persistently track the need to do a full init
    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
    HashSet<String> mPendingInits = new HashSet<String>();  // transport names
    ArraySet<String> mPendingInits = new ArraySet<String>();  // transport names
    // Round-robin queue for scheduling full backup passes
    // Round-robin queue for scheduling full backup passes
    static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
    static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
@@ -1044,20 +1044,6 @@ public class BackupManagerService implements BackupManagerServiceInterface {
                break;
                break;
            }
            }
            case MSG_RUN_INITIALIZE:
            {
                HashSet<String> queue;
                // Snapshot the pending-init queue and work on that
                synchronized (mQueueLock) {
                    queue = new HashSet<String>(mPendingInits);
                    mPendingInits.clear();
                }
                (new PerformInitializeTask(queue)).run();
                break;
            }
            case MSG_RETRY_INIT:
            case MSG_RETRY_INIT:
            {
            {
                synchronized (mQueueLock) {
                synchronized (mQueueLock) {
@@ -1321,7 +1307,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
        // Set up the backup-request journaling
        // Set up the backup-request journaling
        mJournalDir = new File(mBaseStateDir, "pending");
        mJournalDir = new File(mBaseStateDir, "pending");
@@ -1408,15 +1394,15 @@ public class BackupManagerService implements BackupManagerServiceInterface {
    private class RunInitializeReceiver extends BroadcastReceiver {
    private class RunInitializeReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
                // Snapshot the pending-init queue and work on that
                synchronized (mQueueLock) {
                synchronized (mQueueLock) {
                    if (DEBUG) Slog.v(TAG, "Running a device init");
                    String[] queue = mPendingInits.toArray(new String[mPendingInits.size()]);
                    mPendingInits.clear();
                    // Acquire the wakelock and pass it to the init thread.  it will
                    // Acquire the wakelock and pass it to the init thread.  it will
                    // be released once init concludes.
                    // be released once init concludes.
                    mWakelock.acquire();
                    mWakelock.acquire();
                    mBackupHandler.post(new PerformInitializeTask(queue, null));
                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
                    mBackupHandler.sendMessage(msg);
                }
                }
            }
            }
        }
        }
@@ -9768,13 +9754,37 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
    }
    }
    class PerformInitializeTask implements Runnable {
    class PerformInitializeTask implements Runnable {
        HashSet<String> mQueue;
        String[] mQueue;
        IBackupObserver mObserver;
        PerformInitializeTask(HashSet<String> transportNames) {
        PerformInitializeTask(String[] transportNames, IBackupObserver observer) {
            mQueue = transportNames;
            mQueue = transportNames;
            mObserver = observer;
        }
        private void notifyResult(String target, int status) {
            try {
                if (mObserver != null) {
                    mObserver.onResult(target, status);
                }
            } catch (RemoteException ignored) {
                mObserver = null;       // don't try again
            }
        }
        private void notifyFinished(int status) {
            try {
                if (mObserver != null) {
                    mObserver.backupFinished(status);
                }
            } catch (RemoteException ignored) {
                mObserver = null;
            }
        }
        }
        public void run() {
        public void run() {
            // mWakelock is *acquired* when execution begins here
            int result = BackupTransport.TRANSPORT_OK;
            try {
            try {
                for (String transportName : mQueue) {
                for (String transportName : mQueue) {
                    IBackupTransport transport =
                    IBackupTransport transport =
@@ -9803,6 +9813,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                        synchronized (mQueueLock) {
                        synchronized (mQueueLock) {
                            recordInitPendingLocked(false, transportName);
                            recordInitPendingLocked(false, transportName);
                        }
                        }
                        notifyResult(transportName, BackupTransport.TRANSPORT_OK);
                    } else {
                    } else {
                        // If this didn't work, requeue this one and try again
                        // If this didn't work, requeue this one and try again
                        // after a suitable interval
                        // after a suitable interval
@@ -9811,6 +9822,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                        synchronized (mQueueLock) {
                        synchronized (mQueueLock) {
                            recordInitPendingLocked(true, transportName);
                            recordInitPendingLocked(true, transportName);
                        }
                        }
                        notifyResult(transportName, status);
                        result = status;
                        // do this via another alarm to make sure of the wakelock states
                        // do this via another alarm to make sure of the wakelock states
                        long delay = transport.requestBackupTime();
                        long delay = transport.requestBackupTime();
                        Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
                        Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
@@ -9820,8 +9834,10 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                }
                }
            } catch (Exception e) {
            } catch (Exception e) {
                Slog.e(TAG, "Unexpected error performing init", e);
                Slog.e(TAG, "Unexpected error performing init", e);
                result = BackupTransport.TRANSPORT_ERROR;
            } finally {
            } finally {
                // Done; release the wakelock
                // Done; release the wakelock
                notifyFinished(result);
                mWakelock.release();
                mWakelock.release();
            }
            }
        }
        }
@@ -9939,6 +9955,23 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
            });
            });
    }
    }
    // Run an initialize operation for the given transport
    @Override
    public void initializeTransports(String[] transportNames, IBackupObserver observer) {
        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "initializeTransport");
        if (MORE_DEBUG) {
            Slog.v(TAG, "initializeTransports() of " + transportNames);
        }
        final long oldId = Binder.clearCallingIdentity();
        try {
            mWakelock.acquire();
            mBackupHandler.post(new PerformInitializeTask(transportNames, observer));
        } finally {
            Binder.restoreCallingIdentity(oldId);
        }
    }
    // Clear the given package's backup data from the current transport
    // Clear the given package's backup data from the current transport
    @Override
    @Override
    public void clearBackupData(String transportName, String packageName) {
    public void clearBackupData(String transportName, String packageName) {
+3 −0
Original line number Original line Diff line number Diff line
@@ -77,6 +77,9 @@ public interface BackupManagerServiceInterface {


  void dataChanged(String packageName);
  void dataChanged(String packageName);


  // Initialize the given transport
  void initializeTransports(String[] transportName, IBackupObserver observer);

  // Clear the given package's backup data from the current transport
  // Clear the given package's backup data from the current transport
  void clearBackupData(String transportName, String packageName);
  void clearBackupData(String transportName, String packageName);


Loading