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

Commit 31c8e08e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add 'bmgr' command to synchronously init transports"

parents a1dd7191 924afe2b
Loading
Loading
Loading
Loading
+93 −18
Original line number Diff line number Diff line
@@ -17,8 +17,8 @@
package com.android.commands.bmgr;

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.IBackupObserver;
import android.app.backup.IRestoreObserver;
@@ -30,8 +30,11 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
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.HashSet;
@@ -97,6 +100,11 @@ public final class Bmgr {
            return;
        }

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

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

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

    class BackupObserver extends IBackupObserver.Stub {
        boolean done = false;
    // IBackupObserver generically usable for any backup/init operation
    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) {
            System.out.println(
                "Package " + currentPackage + " with progress: " + backupProgress.bytesTransferred
                    + "/" + backupProgress.bytesExpected);
        }

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

        @Override
        public void backupFinished(int status) {
            System.out.println("Backup finished with result: "
                    + convertBackupStatusToString(status));
            synchronized (this) {
            synchronized (trigger) {
                done = true;
                this.notify();
                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.
            synchronized (this) {
                while (!this.done) {
            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 {
                        this.wait();
                        trigger.wait(1000L);
                    } 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) {
@@ -349,9 +389,11 @@ public final class Bmgr {
            } else if (pkg.equals("--incremental")) {
                nonIncrementalBackup = false;
            } else {
                if (!allPkgs.contains(pkg)) {
                    allPkgs.add(pkg);
                }
            }
        }
        if (backupAll) {
            if (allPkgs.size() == 0) {
                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() {
        String arg = nextArg();     // sets, transports, packages set#
        if ("transports".equals(arg)) {
+9 −0
Original line number Diff line number Diff line
@@ -56,6 +56,15 @@ interface IBackupManager {
     */
    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
     * method is only invoked by the Activity Manager.
+8 −6
Original line number 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.
     * 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.
     */
    void onUpdate(String currentPackage, in BackupProgress backupProgress);

    /**
     * The backup of single package has completed.  This method will be called at most one time
     * for each package and could be not called if backup is failed before and
     * backupFinished() is called.
     * Backup of one package or initialization of one transport has completed.  This
     * method will be called at most one time for each package or transport, and might not
     * 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.
     */
    void onResult(String currentPackage, int status);
    void onResult(String target, int status);

    /**
     * The backup process has completed.  This method will always be called,
+57 −24
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
@@ -123,8 +124,8 @@ import com.android.server.EventLogTags;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import com.android.server.power.BatterySaverPolicy.ServiceType;
import libcore.io.IoUtils;
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_RESTORE = 3;
    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_RESTORE_SESSION_TIMEOUT = 8;
    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
    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
    static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
@@ -1044,20 +1044,6 @@ public class BackupManagerService implements BackupManagerServiceInterface {
                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:
            {
                synchronized (mQueueLock) {
@@ -1321,7 +1307,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
        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
        mJournalDir = new File(mBaseStateDir, "pending");
@@ -1408,15 +1394,15 @@ public class BackupManagerService implements BackupManagerServiceInterface {
    private class RunInitializeReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
                // Snapshot the pending-init queue and work on that
                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
                    // be released once init concludes.
                    mWakelock.acquire();
                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
                    mBackupHandler.sendMessage(msg);
                    mBackupHandler.post(new PerformInitializeTask(queue, null));
                }
            }
        }
@@ -9768,13 +9754,37 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
    }
    class PerformInitializeTask implements Runnable {
        HashSet<String> mQueue;
        String[] mQueue;
        IBackupObserver mObserver;
        PerformInitializeTask(HashSet<String> transportNames) {
        PerformInitializeTask(String[] transportNames, IBackupObserver observer) {
            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() {
            // mWakelock is *acquired* when execution begins here
            int result = BackupTransport.TRANSPORT_OK;
            try {
                for (String transportName : mQueue) {
                    IBackupTransport transport =
@@ -9803,6 +9813,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                        synchronized (mQueueLock) {
                            recordInitPendingLocked(false, transportName);
                        }
                        notifyResult(transportName, BackupTransport.TRANSPORT_OK);
                    } else {
                        // If this didn't work, requeue this one and try again
                        // after a suitable interval
@@ -9811,6 +9822,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
                        synchronized (mQueueLock) {
                            recordInitPendingLocked(true, transportName);
                        }
                        notifyResult(transportName, status);
                        result = status;
                        // do this via another alarm to make sure of the wakelock states
                        long delay = transport.requestBackupTime();
                        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) {
                Slog.e(TAG, "Unexpected error performing init", e);
                result = BackupTransport.TRANSPORT_ERROR;
            } finally {
                // Done; release the wakelock
                notifyFinished(result);
                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
    @Override
    public void clearBackupData(String transportName, String packageName) {
+3 −0
Original line number Diff line number Diff line
@@ -77,6 +77,9 @@ public interface BackupManagerServiceInterface {

  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
  void clearBackupData(String transportName, String packageName);

Loading