Merge "Handle split rotation in rotation callback"
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index 567eb4a..9bb048d 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -25,6 +25,11 @@
 import android.os.RemoteException;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.backup.ITransportStatusCallback;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Concrete class that provides a stable-API bridge between IBackupTransport
@@ -659,141 +664,185 @@
     class TransportImpl extends IBackupTransport.Stub {
 
         @Override
-        public String name() throws RemoteException {
-            return BackupTransport.this.name();
+        public void name(AndroidFuture<String> resultFuture) throws RemoteException {
+            String result = BackupTransport.this.name();
+            resultFuture.complete(result);
         }
 
         @Override
-        public Intent configurationIntent() throws RemoteException {
-            return BackupTransport.this.configurationIntent();
-        }
-
-        @Override
-        public String currentDestinationString() throws RemoteException {
-            return BackupTransport.this.currentDestinationString();
-        }
-
-        @Override
-        public Intent dataManagementIntent() {
-            return BackupTransport.this.dataManagementIntent();
-        }
-
-        @Override
-        public CharSequence dataManagementIntentLabel() {
-            return BackupTransport.this.dataManagementIntentLabel();
-        }
-
-        @Override
-        public String transportDirName() throws RemoteException {
-            return BackupTransport.this.transportDirName();
-        }
-
-        @Override
-        public long requestBackupTime() throws RemoteException {
-            return BackupTransport.this.requestBackupTime();
-        }
-
-        @Override
-        public int initializeDevice() throws RemoteException {
-            return BackupTransport.this.initializeDevice();
-        }
-
-        @Override
-        public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
+        public void configurationIntent(AndroidFuture<Intent> resultFuture)
                 throws RemoteException {
-            return BackupTransport.this.performBackup(packageInfo, inFd, flags);
+            Intent result = BackupTransport.this.configurationIntent();
+            resultFuture.complete(result);
         }
 
         @Override
-        public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
-            return BackupTransport.this.clearBackupData(packageInfo);
+        public void currentDestinationString(AndroidFuture<String> resultFuture)
+                throws RemoteException {
+            String result = BackupTransport.this.currentDestinationString();
+            resultFuture.complete(result);
         }
 
         @Override
-        public int finishBackup() throws RemoteException {
-            return BackupTransport.this.finishBackup();
+        public void dataManagementIntent(AndroidFuture<Intent> resultFuture)
+                throws RemoteException {
+            Intent result = BackupTransport.this.dataManagementIntent();
+            resultFuture.complete(result);
         }
 
         @Override
-        public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
-            return BackupTransport.this.getAvailableRestoreSets();
+        public void dataManagementIntentLabel(AndroidFuture<CharSequence> resultFuture)
+                throws RemoteException {
+            CharSequence result = BackupTransport.this.dataManagementIntentLabel();
+            resultFuture.complete(result);
         }
 
         @Override
-        public long getCurrentRestoreSet() throws RemoteException {
-            return BackupTransport.this.getCurrentRestoreSet();
+        public void transportDirName(AndroidFuture<String> resultFuture) throws RemoteException {
+            String result = BackupTransport.this.transportDirName();
+            resultFuture.complete(result);
         }
 
         @Override
-        public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
-            return BackupTransport.this.startRestore(token, packages);
+        public void requestBackupTime(AndroidFuture<Long> resultFuture) throws RemoteException {
+            long result = BackupTransport.this.requestBackupTime();
+            resultFuture.complete(result);
         }
 
         @Override
-        public RestoreDescription nextRestorePackage() throws RemoteException {
-            return BackupTransport.this.nextRestorePackage();
+        public void initializeDevice(ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.initializeDevice();
+            callback.onOperationCompleteWithStatus(result);
         }
 
         @Override
-        public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
-            return BackupTransport.this.getRestoreData(outFd);
+        public void performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags,
+                ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.performBackup(packageInfo, inFd, flags);
+            callback.onOperationCompleteWithStatus(result);
         }
 
         @Override
-        public void finishRestore() throws RemoteException {
+        public void clearBackupData(PackageInfo packageInfo, ITransportStatusCallback callback)
+                throws RemoteException {
+            int result = BackupTransport.this.clearBackupData(packageInfo);
+            callback.onOperationCompleteWithStatus(result);
+        }
+
+        @Override
+        public void finishBackup(ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.finishBackup();
+            callback.onOperationCompleteWithStatus(result);
+        }
+
+        @Override
+        public void getAvailableRestoreSets(AndroidFuture<List<RestoreSet>> resultFuture)
+                throws RemoteException {
+            RestoreSet[] result = BackupTransport.this.getAvailableRestoreSets();
+            resultFuture.complete(Arrays.asList(result));
+        }
+
+        @Override
+        public void getCurrentRestoreSet(AndroidFuture<Long> resultFuture)
+                throws RemoteException {
+            long result = BackupTransport.this.getCurrentRestoreSet();
+            resultFuture.complete(result);
+        }
+
+        @Override
+        public void startRestore(long token, PackageInfo[] packages,
+                ITransportStatusCallback callback)  throws RemoteException {
+            int result = BackupTransport.this.startRestore(token, packages);
+            callback.onOperationCompleteWithStatus(result);
+        }
+
+        @Override
+        public void nextRestorePackage(AndroidFuture<RestoreDescription> resultFuture)
+                throws RemoteException {
+            RestoreDescription result = BackupTransport.this.nextRestorePackage();
+            resultFuture.complete(result);
+        }
+
+        @Override
+        public void getRestoreData(ParcelFileDescriptor outFd,
+                ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.getRestoreData(outFd);
+            callback.onOperationCompleteWithStatus(result);
+        }
+
+        @Override
+        public void finishRestore(ITransportStatusCallback callback)
+                throws RemoteException {
             BackupTransport.this.finishRestore();
+            callback.onOperationComplete();
         }
 
         @Override
-        public long requestFullBackupTime() throws RemoteException {
-            return BackupTransport.this.requestFullBackupTime();
-        }
-
-        @Override
-        public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
-                int flags) throws RemoteException {
-            return BackupTransport.this.performFullBackup(targetPackage, socket, flags);
-        }
-
-        @Override
-        public int checkFullBackupSize(long size) {
-            return BackupTransport.this.checkFullBackupSize(size);
-        }
-
-        @Override
-        public int sendBackupData(int numBytes) throws RemoteException {
-            return BackupTransport.this.sendBackupData(numBytes);
-        }
-
-        @Override
-        public void cancelFullBackup() throws RemoteException {
-            BackupTransport.this.cancelFullBackup();
-        }
-
-        @Override
-        public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
+        public void requestFullBackupTime(AndroidFuture<Long> resultFuture)
                 throws RemoteException {
-            return BackupTransport.this.isAppEligibleForBackup(targetPackage, isFullBackup);
+            long result = BackupTransport.this.requestFullBackupTime();
+            resultFuture.complete(result);
         }
 
         @Override
-        public long getBackupQuota(String packageName, boolean isFullBackup) {
-            return BackupTransport.this.getBackupQuota(packageName, isFullBackup);
+        public void performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
+                int flags, ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.performFullBackup(targetPackage, socket, flags);
+            callback.onOperationCompleteWithStatus(result);
         }
 
         @Override
-        public int getTransportFlags() {
-            return BackupTransport.this.getTransportFlags();
+        public void checkFullBackupSize(long size, ITransportStatusCallback callback)
+                throws RemoteException {
+            int result = BackupTransport.this.checkFullBackupSize(size);
+            callback.onOperationCompleteWithStatus(result);
         }
 
         @Override
-        public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
-            return BackupTransport.this.getNextFullRestoreDataChunk(socket);
+        public void sendBackupData(int numBytes, ITransportStatusCallback callback)
+                throws RemoteException {
+            int result = BackupTransport.this.sendBackupData(numBytes);
+            callback.onOperationCompleteWithStatus(result);
         }
 
         @Override
-        public int abortFullRestore() {
-            return BackupTransport.this.abortFullRestore();
+        public void cancelFullBackup(ITransportStatusCallback callback) throws RemoteException {
+            BackupTransport.this.cancelFullBackup();
+            callback.onOperationComplete();
+        }
+
+        @Override
+        public void isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup,
+                AndroidFuture<Boolean> resultFuture) throws RemoteException {
+            boolean result = BackupTransport.this.isAppEligibleForBackup(targetPackage,
+                    isFullBackup);
+            resultFuture.complete(result);
+        }
+
+        @Override
+        public void getBackupQuota(String packageName, boolean isFullBackup,
+                AndroidFuture<Long> resultFuture) throws RemoteException {
+            long result = BackupTransport.this.getBackupQuota(packageName, isFullBackup);
+            resultFuture.complete(result);
+        }
+
+        @Override
+        public void getTransportFlags(AndroidFuture<Integer> resultFuture) throws RemoteException {
+            int result = BackupTransport.this.getTransportFlags();
+            resultFuture.complete(result);
+        }
+
+        @Override
+        public void getNextFullRestoreDataChunk(ParcelFileDescriptor socket,
+                ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.getNextFullRestoreDataChunk(socket);
+            callback.onOperationCompleteWithStatus(result);
+        }
+
+        @Override
+        public void abortFullRestore(ITransportStatusCallback callback) throws RemoteException {
+            int result = BackupTransport.this.abortFullRestore();
+            callback.onOperationCompleteWithStatus(result);
         }
     }
 }
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index c9baf00..f09e176 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -22,39 +22,46 @@
 import android.content.pm.PackageInfo;
 import android.os.ParcelFileDescriptor;
 
+import com.android.internal.backup.ITransportStatusCallback;
+import com.android.internal.infra.AndroidFuture;
+
 /** {@hide} */
-interface IBackupTransport {
+oneway interface IBackupTransport {
     /**
-     * Ask the transport for the name under which it should be registered.  This will
+     * Ask the transport for the String name under which it should be registered.  This will
      * typically be its host service's component name, but need not be.
+     *
+     * @param resultFuture an {@link AndroidFuture} that is completed with the {@code String} name
+     * of the transport.
      */
-    String name();
+    void name(in AndroidFuture<String> result);
 
-	/**
-	 * Ask the transport for an Intent that can be used to launch any internal
-	 * configuration Activity that it wishes to present.  For example, the transport
-	 * may offer a UI for allowing the user to supply login credentials for the
-	 * transport's off-device backend.
-	 *
-	 * If the transport does not supply any user-facing configuration UI, it should
-	 * return null from this method.
-	 *
-	 * @return An Intent that can be passed to Context.startActivity() in order to
-	 *         launch the transport's configuration UI.  This method will return null
-	 *         if the transport does not offer any user-facing configuration UI.
-	 */
-	Intent configurationIntent();
+    /**
+     * Ask the transport for an Intent that can be used to launch any internal
+     * configuration Activity that it wishes to present.  For example, the transport
+     * may offer a UI for allowing the user to supply login credentials for the
+     * transport's off-device backend.
+     *
+     * If the transport does not supply any user-facing configuration UI, it should
+     * return null from this method.
+     *
+     * @param resultFuture an {@link AndroidFuture} that is completed with an {@code Intent} that
+     *        can be passed to Context.startActivity() in order to launch the transport's
+     *        configuration UI.  This future will complete with null if the transport does not
+     *        offer any user-facing configuration UI.
+     */
+    void configurationIntent(in AndroidFuture<Intent> resultFuture);
 
-	/**
-	 * On demand, supply a one-line string that can be shown to the user that
-	 * describes the current backend destination.  For example, a transport that
-	 * can potentially associate backup data with arbitrary user accounts should
-	 * include the name of the currently-active account here.
-	 *
-	 * @return A string describing the destination to which the transport is currently
-	 *         sending data.  This method should not return null.
-	 */
-	String currentDestinationString();
+    /**
+     * Ask the transport for a one-line string that can be shown to the user that
+     * describes the current backend destination.  For example, a transport that
+     * can potentially associate backup data with arbitrary user accounts should
+     * include the name of the currently-active account here.
+     *
+     * @param resultFuture an {@link AndroidFuture} that is completed with a {@code String}
+     *        describing the destination to which the transport is currently sending data.
+     */
+    void currentDestinationString(in AndroidFuture<String> resultFuture);
 
     /**
      * Ask the transport for an Intent that can be used to launch a more detailed
@@ -71,22 +78,23 @@
      * <p>If the transport does not supply any user-facing data management
      * UI, then it should return {@code null} from this method.
      *
-     * @return An intent that can be passed to Context.startActivity() in order to
-     *         launch the transport's data-management UI.  This method will return
-     *         {@code null} if the transport does not offer any user-facing data
-     *         management UI.
+     * @param resultFuture an {@link AndroidFuture} that is completed with an {@code Intent} that
+     *        can be passed to Context.startActivity() in order to launch the transport's
+     *        data-management UI. The callback will supply {@code null} if the transport does not
+     *        offer any user-facing data management UI.
      */
-    Intent dataManagementIntent();
+    void dataManagementIntent(in AndroidFuture<Intent> resultFuture);
 
     /**
-     * On demand, supply a short {@link CharSequence} that can be shown to the user as the label on
-     * an overflow menu item used to invoke the data management UI.
+     * Ask the transport for a short {@link CharSequence} that can be shown to the user as the label
+     * on an overflow menu item used to invoke the data management UI.
      *
-     * @return A {@link CharSequence} to be used as the label for the transport's data management
-     *         affordance.  If the transport supplies a data management intent, this
-     *         method must not return {@code null}.
+     * @param resultFuture an {@link AndroidFuture} that is completed with a {@code CharSequence}
+     *        to be used as the label for the transport's data management affordance.  If the
+     *        transport supplies a data management Intent via {@link #dataManagementIntent},
+     *        this method must not return {@code null}.
      */
-    CharSequence dataManagementIntentLabel();
+    void dataManagementIntentLabel(in AndroidFuture<CharSequence> resultFuture);
 
     /**
      * Ask the transport where, on local device storage, to keep backup state blobs.
@@ -96,11 +104,11 @@
      * available backup transports; the name of the class implementing the transport
      * is a good choice.  This MUST be constant.
      *
-     * @return A unique name, suitable for use as a file or directory name, that the
-     *         Backup Manager could use to disambiguate state files associated with
-     *         different backup transports.
+     * @param resultFuture an {@link AndroidFuture} that is completed with a unique {@code String}
+     *        name, suitable for use as a file or directory name, that the Backup Manager could use
+     *        to disambiguate state files associated with different backup transports.
      */
-    String transportDirName();
+    void transportDirName(in AndroidFuture<String> resultFuture);
 
     /**
      * Verify that this is a suitable time for a backup pass.  This should return zero
@@ -110,10 +118,11 @@
      * <p>If this is not a suitable time for a backup, the transport should return a
      * backoff delay, in milliseconds, after which the Backup Manager should try again.
      *
-     * @return Zero if this is a suitable time for a backup pass, or a positive time delay
-     *   in milliseconds to suggest deferring the backup pass for a while.
+     * @param resultFuture an {@link AndroidFuture} that is completed with {@code int}: zero if
+     *        this is a suitable time for a backup pass, or a positive time delay in milliseconds
+     *        to suggest deferring the backup pass for a while.
      */
-    long requestBackupTime();
+    void requestBackupTime(in AndroidFuture<long> resultFuture);
 
     /**
      * Initialize the server side storage for this device, erasing all stored data.
@@ -121,10 +130,11 @@
      * this is called, {@link #finishBackup} must be called to ensure the request
      * is sent and received successfully.
      *
-     * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
-     *   {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
+     * @param callback a callback that is completed with a {@code int} which is one
+     *        of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
+     *        {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
      */
-    int initializeDevice();
+    void initializeDevice(in ITransportStatusCallback callback);
 
     /**
      * Send one application's data to the backup destination.  The transport may send
@@ -137,12 +147,14 @@
      *   BackupService.doBackup() method.  This may be a pipe rather than a file on
      *   persistent media, so it may not be seekable.
      * @param flags Some of {@link BackupTransport#FLAG_USER_INITIATED}.
-     * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
-     *  {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
-     *  {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
-     *  become lost due to inactive expiry or some other reason and needs re-initializing)
+     * @param callback a callback that is completed with a {@code int} which is one
+     *  of {@link BackupConstants#TRANSPORT_OK}(OK so far), {@link BackupConstants#TRANSPORT_ERROR}
+     *  (on network error or other failure), or {@link BackupConstants#TRANSPORT_NOT_INITIALIZED}
+     *  (if the backend dataset has become lost due to inactive expiry or some other reason and
+     *  needs re-initializing).
      */
-    int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, int flags);
+    void performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd, int flags,
+            in ITransportStatusCallback callback);
 
     /**
      * Erase the give application's data from the backup destination.  This clears
@@ -150,9 +162,10 @@
      * the app had never yet been backed up.  After this is called, {@link finishBackup}
      * must be called to ensure that the operation is recorded successfully.
      *
-     * @return the same error codes as {@link #performBackup}.
+     * @param callback a callback that is completed with the same error codes as
+     *        {@link #performBackup}.
      */
-    int clearBackupData(in PackageInfo packageInfo);
+    void clearBackupData(in PackageInfo packageInfo, in ITransportStatusCallback callback);
 
     /**
      * Finish sending application data to the backup destination.  This must be
@@ -160,27 +173,30 @@
      * all data is sent.  Only when this method returns true can a backup be assumed
      * to have succeeded.
      *
-     * @return the same error codes as {@link #performBackup}.
+     * @param callback a callback that is completed with the same error codes as
+     *        {@link #performBackup}.
      */
-    int finishBackup();
+    void finishBackup(in ITransportStatusCallback callback);
 
     /**
      * Get the set of all backups currently available over this transport.
      *
-     * @return Descriptions of the set of restore images available for this device,
-     *   or null if an error occurred (the attempt should be rescheduled).
+     * @param resultFuture an {@link AndroidFuture} that is completed with {@code List<RestoreSet>}:
+     *        the descriptions of a set of restore images available for this device, or null if an
+     *        error occurred (the attempt should be rescheduled).
      **/
-    RestoreSet[] getAvailableRestoreSets();
+    void getAvailableRestoreSets(in AndroidFuture<List<RestoreSet>> resultFuture);
 
     /**
      * Get the identifying token of the backup set currently being stored from
      * this device.  This is used in the case of applications wishing to restore
      * their last-known-good data.
      *
-     * @return A token that can be passed to {@link #startRestore}, or 0 if there
-     *   is no backup set available corresponding to the current device state.
+     * @param resultFuture an {@link AndroidFuture} that is completed with a {@code long}: a token
+     *        that can be passed to {@link #startRestore}, or 0 if there is no backup set available
+     *        corresponding to the current device state.
      */
-    long getCurrentRestoreSet();
+    void getCurrentRestoreSet(in AndroidFuture<long> resultFuture);
 
     /**
      * Start restoring application data from backup.  After calling this function,
@@ -191,11 +207,12 @@
      *   or {@link #getCurrentRestoreSet}.
      * @param packages List of applications to restore (if data is available).
      *   Application data will be restored in the order given.
-     * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
-     *   {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
-     *   (an error occurred, the restore should be aborted and rescheduled).
+     * @param callback a callback that is completed with one of
+     *   {@link BackupConstants#TRANSPORT_OK} (OK so far, call {@link #nextRestorePackage}) or
+     *   {@link BackupConstants#TRANSPORT_ERROR} (an error occurred, the restore should be aborted
+     *   and rescheduled).
      */
-    int startRestore(long token, in PackageInfo[] packages);
+    void startRestore(long token, in PackageInfo[] packages, in ITransportStatusCallback callback);
 
     /**
      * Get the package name of the next application with data in the backup store, plus
@@ -210,34 +227,95 @@
      * <p>If this method returns {@code null}, it means that a transport-level error has
      * occurred and the entire restore operation should be abandoned.
      *
-     * @return A RestoreDescription object containing the name of one of the packages
-     *   supplied to {@link #startRestore} plus an indicator of the data type of that
-     *   restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
-     *   no more packages can be restored in this session; or {@code null} to indicate
-     *   a transport-level error.
+     * @param resultFuture an {@link AndroidFuture} that is completed with a
+     *   {@link RestoreDescription} object containing the name of one of the packages supplied to
+     *   {@link #startRestore} plus an indicator of the data type of that restore data; or
+     *   {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that no more packages can be
+     *   restored in this session; or {@code null} to indicate a transport-level error.
      */
-    RestoreDescription nextRestorePackage();
+    void nextRestorePackage(in AndroidFuture<RestoreDescription> resultFuture);
 
     /**
      * Get the data for the application returned by {@link #nextRestorePackage}.
      * @param data An open, writable file into which the backup data should be stored.
-     * @return the same error codes as {@link #startRestore}.
+     *
+     * @param callback a callback that is completed with the same error codes as
+     *        {@link #startRestore}.
      */
-    int getRestoreData(in ParcelFileDescriptor outFd);
+    void getRestoreData(in ParcelFileDescriptor outFd, in ITransportStatusCallback callback);
 
     /**
      * End a restore session (aborting any in-process data transfer as necessary),
      * freeing any resources and connections used during the restore process.
+     *
+     * @param callback a callback to signal that restore has been finished on transport side.
      */
-    void finishRestore();
+    void finishRestore(in ITransportStatusCallback callback);
 
     // full backup stuff
 
-    long requestFullBackupTime();
-    int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket, int flags);
-    int checkFullBackupSize(long size);
-    int sendBackupData(int numBytes);
-    void cancelFullBackup();
+    /**
+     * Verify that this is a suitable time for a full-data backup pass.
+     *
+     * @param resultFuture an {@link AndroidFuture} that is completed with {@code long}: 0 if this
+     *        is a suitable time for a backup pass, or a positive time delay in milliseconds to
+     *        suggest deferring the backup pass for a while.
+     */
+    void requestFullBackupTime(in AndroidFuture<long> resultFuture);
+
+    /**
+     * Begin the process of sending an application's full-data archive to the backend.
+     *
+     * @param targetPackage The package whose data is to follow.
+     * @param socket The socket file descriptor through which the data will be provided.
+     * @param flags {@link BackupTransport#FLAG_USER_INITIATED} or 0.
+     * @param callback callback to return a {@code int} which is one of:
+     *        {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} to indicate that the stated
+     *        application is not to be backed up; {@link BackupTransport#TRANSPORT_OK} to indicate
+     *        that the OS may proceed with delivering backup data;
+     *        {@link BackupTransport#TRANSPORT_ERROR to indicate a fatal error condition that
+     *        precludes performing a backup at this time.
+     */
+    void performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket, int flags,
+            in ITransportStatusCallback callback);
+
+    /**
+     * Called after {@link #performFullBackup} to make sure that the transport is willing to
+     * handle a full-data backup operation of the specified size on the current package.
+     *
+     * @param size The estimated size of the full-data payload for this app.  This includes
+     *         manifest and archive format overhead, but is not guaranteed to be precise.
+     * @param callback a callback that is completed with a {@code int} which is
+     *        one of: {@link BackupTransport#TRANSPORT_OK} if the platform is to proceed with the
+     *        full-data {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} if the proposed payload
+     *        size is backup, too large for the transport to handle, or
+     *        {@link BackupTransport#TRANSPORT_ERROR} to indicate a fatal error condition that
+     *        means the platform cannot perform a backup at this time.
+     */
+    void checkFullBackupSize(long size, in ITransportStatusCallback callback);
+
+    /**
+     * Tells the transport to read {@code numBytes} bytes of data from the socket file
+     * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
+     * call, and deliver those bytes to the datastore.
+     *
+     * @param numBytes The number of bytes of tarball data available to be read from the
+     *    socket.
+     * @param callback a callback that is completed with a {@code int} which is
+     *        one of: {@link BackupTransport#TRANSPORT_OK} on successful processing of the data,
+     *        {@link BackupTransport#TRANSPORT_ERROR} to indicate a fatal error situation.  If an
+     *        error is returned, the system will call finishBackup() and stop attempting backups
+     *        until after a backoff and retry interval.
+     */
+    void sendBackupData(int numBytes, in ITransportStatusCallback callback);
+
+    /**
+     * Tells the transport to cancel the currently-ongoing full backup operation.
+     *
+     * @param callback a callback to indicate that transport has cancelled the operation,
+     *        does not return any value (see {@link ITransportCallback#onVoidReceived}).
+     */
+    void cancelFullBackup(in ITransportStatusCallback callback);
 
     /**
      * Ask the transport whether this app is eligible for backup.
@@ -245,9 +323,11 @@
      * @param targetPackage The identity of the application.
      * @param isFullBackup If set, transport should check if app is eligible for full data backup,
      *   otherwise to check if eligible for key-value backup.
-     * @return Whether this app is eligible for backup.
+     * @param resultFuture an {@link AndroidFuture} that is completed with a {@code boolean}
+     *        indicating whether this app is eligible for backup.
      */
-    boolean isAppEligibleForBackup(in PackageInfo targetPackage, boolean isFullBackup);
+    void isAppEligibleForBackup(in PackageInfo targetPackage, boolean isFullBackup,
+            in AndroidFuture<boolean> resultFuture);
 
     /**
      * Ask the transport about current quota for backup size of the package.
@@ -255,9 +335,11 @@
      * @param packageName ID of package to provide the quota.
      * @param isFullBackup If set, transport should return limit for full data backup, otherwise
      *                     for key-value backup.
-     * @return Current limit on full data backup size in bytes.
+     * @param resultFuture an {@link AndroidFuture} that is completed with a {@code long}: current
+     *        limit on full data backup size in bytes.
      */
-    long getBackupQuota(String packageName, boolean isFullBackup);
+    void getBackupQuota(String packageName, boolean isFullBackup,
+            in AndroidFuture<long> resultFuture);
 
     // full restore stuff
 
@@ -284,13 +366,14 @@
      * @param socket The file descriptor that the transport will use for delivering the
      *    streamed archive.  The transport must close this socket in all cases when returning
      *    from this method.
-     * @return 0 when no more data for the current package is available.  A positive value
-     *    indicates the presence of that many bytes to be delivered to the app.  Any negative
-     *    return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
-     *    indicating a fatal error condition that precludes further restore operations
-     *    on the current dataset.
+     * @param callback a callback that is completed with an {@code int}: 0 when
+     *    no more data for the current package is available.  A positive value indicates the
+     *    presence of that many bytes to be delivered to the app.  Any negative return value is
+     *    treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, indicating a fatal error
+     *    condition that precludes further restore operations on the current dataset.
      */
-    int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket);
+    void getNextFullRestoreDataChunk(in ParcelFileDescriptor socket,
+            in ITransportStatusCallback callback);
 
     /**
      * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
@@ -300,19 +383,21 @@
      * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
      * operation.
      *
-     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
-     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
-     *    transport-level failure.  If the transport reports an error here, the entire restore
-     *    operation will immediately be finished with no further attempts to restore app data.
+     * @param callback a callback that is completed with {@code int}, which is
+     *    one of: {@link #TRANSPORT_OK} if the transport was successful in shutting down the current
+     *    stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious transport-level failure.
+     *    If the transport reports an error here, the entire restore operation will immediately be
+     *    finished with no further attempts to restore app data.
      */
-    int abortFullRestore();
+    void abortFullRestore(in ITransportStatusCallback callback);
 
     /**
-     * Returns flags with additional information about the transport, which is accessible to the
+     * @param resultFuture an {@link AndroidFuture} that is completed with an {@code int}: flags
+     * with additional information about the transport, which is accessible to the
      * {@link android.app.backup.BackupAgent}. This allows the agent to decide what to backup or
      * restore based on properties of the transport.
      *
      * <p>For supported flags see {@link android.app.backup.BackupAgent}.
      */
-    int getTransportFlags();
+    void getTransportFlags(in AndroidFuture<int> resultFuture);
 }
diff --git a/core/java/com/android/internal/backup/ITransportStatusCallback.aidl b/core/java/com/android/internal/backup/ITransportStatusCallback.aidl
new file mode 100644
index 0000000..a731480
--- /dev/null
+++ b/core/java/com/android/internal/backup/ITransportStatusCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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.internal.backup;
+
+/**
+* A callback class for {@link IBackupTransport}
+*
+* {@hide}
+*/
+oneway interface ITransportStatusCallback {
+    /**
+    * Callback for methods that complete with an {@code int} status.
+    */
+    void onOperationCompleteWithStatus(int status);
+
+    /**
+    * Callback for methods that complete without a value.
+    */
+    void onOperationComplete();
+}
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
index 85ab48c..8963337 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -17,6 +17,7 @@
 package com.android.server.backup.transport;
 
 import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
 import android.app.backup.RestoreDescription;
 import android.app.backup.RestoreSet;
 import android.content.Intent;
@@ -24,50 +25,70 @@
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Client to {@link com.android.internal.backup.IBackupTransport}. Manages the call to the remote
  * transport service and delivers the results.
  */
 public class BackupTransportClient {
+    private static final String TAG = "BackupTransportClient";
+
     private final IBackupTransport mTransportBinder;
+    private final TransportStatusCallbackPool mCallbackPool;
 
     BackupTransportClient(IBackupTransport transportBinder) {
         mTransportBinder = transportBinder;
-
-        // This is a temporary fix to allow blocking calls.
-        // TODO: b/147702043. Redesign IBackupTransport so as to make the calls non-blocking.
-        Binder.allowBlocking(mTransportBinder.asBinder());
+        mCallbackPool = new TransportStatusCallbackPool();
     }
 
     /**
      * See {@link IBackupTransport#name()}.
      */
     public String name() throws RemoteException {
-        return mTransportBinder.name();
+        AndroidFuture<String> resultFuture = new AndroidFuture<>();
+        mTransportBinder.name(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
      * See {@link IBackupTransport#configurationIntent()}
      */
     public Intent configurationIntent() throws RemoteException {
-        return mTransportBinder.configurationIntent();
+        AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+        mTransportBinder.configurationIntent(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
      * See {@link IBackupTransport#currentDestinationString()}
      */
     public String currentDestinationString() throws RemoteException {
-        return mTransportBinder.currentDestinationString();
+        AndroidFuture<String> resultFuture = new AndroidFuture<>();
+        mTransportBinder.currentDestinationString(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
      * See {@link IBackupTransport#dataManagementIntent()}
      */
     public Intent dataManagementIntent() throws RemoteException {
-        return mTransportBinder.dataManagementIntent();
+        AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+        mTransportBinder.dataManagementIntent(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
@@ -75,42 +96,67 @@
      */
     @Nullable
     public CharSequence dataManagementIntentLabel() throws RemoteException {
-        return mTransportBinder.dataManagementIntentLabel();
+        AndroidFuture<CharSequence> resultFuture = new AndroidFuture<>();
+        mTransportBinder.dataManagementIntentLabel(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
      * See {@link IBackupTransport#transportDirName()}
      */
     public String transportDirName() throws RemoteException {
-        return mTransportBinder.transportDirName();
+        AndroidFuture<String> resultFuture = new AndroidFuture<>();
+        mTransportBinder.transportDirName(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
      * See {@link IBackupTransport#initializeDevice()}
      */
     public int initializeDevice() throws RemoteException {
-        return mTransportBinder.initializeDevice();
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.initializeDevice(callback);
+            return callback.getOperationStatus();
+        } finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#clearBackupData(PackageInfo)}
      */
     public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
-        return mTransportBinder.clearBackupData(packageInfo);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.clearBackupData(packageInfo, callback);
+            return callback.getOperationStatus();
+        } finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#finishBackup()}
      */
     public int finishBackup() throws RemoteException {
-        return mTransportBinder.finishBackup();
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.finishBackup(callback);
+            return callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#requestBackupTime()}
      */
     public long requestBackupTime() throws RemoteException {
-        return mTransportBinder.requestBackupTime();
+        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        mTransportBinder.requestBackupTime(resultFuture);
+        Long result = getFutureResult(resultFuture);
+        return result == null ? BackupTransport.TRANSPORT_ERROR : result;
     }
 
     /**
@@ -118,56 +164,91 @@
      */
     public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
             throws RemoteException {
-        return mTransportBinder.performBackup(packageInfo, inFd, flags);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.performBackup(packageInfo, inFd, flags, callback);
+            return callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#getAvailableRestoreSets()}
      */
     public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
-        return mTransportBinder.getAvailableRestoreSets();
+        AndroidFuture<List<RestoreSet>> resultFuture = new AndroidFuture<>();
+        mTransportBinder.getAvailableRestoreSets(resultFuture);
+        List<RestoreSet> result = getFutureResult(resultFuture);
+        return result == null ? null : result.toArray(new RestoreSet[] {});
     }
 
     /**
      * See {@link IBackupTransport#getCurrentRestoreSet()}
      */
     public long getCurrentRestoreSet() throws RemoteException {
-        return mTransportBinder.getCurrentRestoreSet();
+        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        mTransportBinder.getCurrentRestoreSet(resultFuture);
+        Long result = getFutureResult(resultFuture);
+        return result == null ? BackupTransport.TRANSPORT_ERROR : result;
     }
 
     /**
      * See {@link IBackupTransport#startRestore(long, PackageInfo[])}
      */
     public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
-        return mTransportBinder.startRestore(token, packages);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.startRestore(token, packages, callback);
+            return callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#nextRestorePackage()}
      */
     public RestoreDescription nextRestorePackage() throws RemoteException {
-        return mTransportBinder.nextRestorePackage();
+        AndroidFuture<RestoreDescription> resultFuture = new AndroidFuture<>();
+        mTransportBinder.nextRestorePackage(resultFuture);
+        return getFutureResult(resultFuture);
     }
 
     /**
      * See {@link IBackupTransport#getRestoreData(ParcelFileDescriptor)}
      */
     public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
-        return mTransportBinder.getRestoreData(outFd);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.getRestoreData(outFd, callback);
+            return callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#finishRestore()}
      */
     public void finishRestore() throws RemoteException {
-        mTransportBinder.finishRestore();
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.finishRestore(callback);
+            callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#requestFullBackupTime()}
      */
     public long requestFullBackupTime() throws RemoteException {
-        return mTransportBinder.requestFullBackupTime();
+        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        mTransportBinder.requestFullBackupTime(resultFuture);
+        Long result = getFutureResult(resultFuture);
+        return result == null ? BackupTransport.TRANSPORT_ERROR : result;
     }
 
     /**
@@ -175,28 +256,52 @@
      */
     public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
             int flags) throws RemoteException {
-        return mTransportBinder.performFullBackup(targetPackage, socket, flags);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.performFullBackup(targetPackage, socket, flags, callback);
+            return callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#checkFullBackupSize(long)}
      */
     public int checkFullBackupSize(long size) throws RemoteException {
-        return mTransportBinder.checkFullBackupSize(size);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.checkFullBackupSize(size, callback);
+            return callback.getOperationStatus();
+        }  finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#sendBackupData(int)}
      */
     public int sendBackupData(int numBytes) throws RemoteException {
-        return mTransportBinder.sendBackupData(numBytes);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        mTransportBinder.sendBackupData(numBytes, callback);
+        try {
+            return callback.getOperationStatus();
+        } finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#cancelFullBackup()}
      */
     public void cancelFullBackup() throws RemoteException {
-        mTransportBinder.cancelFullBackup();
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.cancelFullBackup(callback);
+            callback.getOperationStatus();
+        } finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
@@ -204,34 +309,93 @@
      */
     public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
             throws RemoteException {
-        return mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup);
+        AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
+        mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup, resultFuture);
+        Boolean result = getFutureResult(resultFuture);
+        return result != null && result;
     }
 
     /**
      * See {@link IBackupTransport#getBackupQuota(String, boolean)}
      */
     public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
-        return mTransportBinder.getBackupQuota(packageName, isFullBackup);
+        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        mTransportBinder.getBackupQuota(packageName, isFullBackup, resultFuture);
+        Long result = getFutureResult(resultFuture);
+        return result == null ? BackupTransport.TRANSPORT_ERROR : result;
     }
 
     /**
      * See {@link IBackupTransport#getNextFullRestoreDataChunk(ParcelFileDescriptor)}
      */
     public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
-        return mTransportBinder.getNextFullRestoreDataChunk(socket);
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.getNextFullRestoreDataChunk(socket, callback);
+            return callback.getOperationStatus();
+        } finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#abortFullRestore()}
      */
     public int abortFullRestore() throws RemoteException {
-        return mTransportBinder.abortFullRestore();
+        TransportStatusCallback callback = mCallbackPool.acquire();
+        try {
+            mTransportBinder.abortFullRestore(callback);
+            return callback.getOperationStatus();
+        } finally {
+            mCallbackPool.recycle(callback);
+        }
     }
 
     /**
      * See {@link IBackupTransport#getTransportFlags()}
      */
     public int getTransportFlags() throws RemoteException {
-        return mTransportBinder.getTransportFlags();
+        AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+        mTransportBinder.getTransportFlags(resultFuture);
+        Integer result = getFutureResult(resultFuture);
+        return result == null ? BackupTransport.TRANSPORT_ERROR : result;
+    }
+
+    private <T> T getFutureResult(AndroidFuture<T> future) {
+        try {
+            return future.get(600, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            Slog.w(TAG, "Failed to get result from transport:", e);
+            return null;
+        }
+    }
+
+    private static class TransportStatusCallbackPool {
+        private static final int MAX_POOL_SIZE = 100;
+
+        private final Object mPoolLock = new Object();
+        private final Queue<TransportStatusCallback> mCallbackPool = new ArrayDeque<>();
+
+        TransportStatusCallback acquire() {
+            synchronized (mPoolLock) {
+                if (mCallbackPool.isEmpty()) {
+                    return new TransportStatusCallback();
+                } else {
+                    return mCallbackPool.poll();
+                }
+            }
+        }
+
+        void recycle(TransportStatusCallback callback) {
+            synchronized (mPoolLock) {
+                if (mCallbackPool.size() > MAX_POOL_SIZE) {
+                    Slog.d(TAG, "TransportStatusCallback pool size exceeded");
+                    return;
+                }
+
+                callback.reset();
+                mCallbackPool.add(callback);
+            }
+        }
     }
 }
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
new file mode 100644
index 0000000..a55178c
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 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.server.backup.transport;
+
+import android.app.backup.BackupTransport;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.ITransportStatusCallback;
+
+public class TransportStatusCallback extends ITransportStatusCallback.Stub {
+    private static final String TAG = "TransportStatusCallback";
+    private static final int TIMEOUT_MILLIS = 600 * 1000; // 10 minutes.
+    private static final int OPERATION_STATUS_DEFAULT = 0;
+
+    private final int mOperationTimeout;
+
+    @GuardedBy("this")
+    private int mOperationStatus = OPERATION_STATUS_DEFAULT;
+    @GuardedBy("this")
+    private boolean mHasCompletedOperation = false;
+
+    public TransportStatusCallback() {
+        mOperationTimeout = TIMEOUT_MILLIS;
+    }
+
+    @VisibleForTesting
+    TransportStatusCallback(int operationTimeout) {
+        mOperationTimeout = operationTimeout;
+    }
+
+    @Override
+    public synchronized void onOperationCompleteWithStatus(int status) throws RemoteException {
+        mHasCompletedOperation = true;
+        mOperationStatus = status;
+
+        notifyAll();
+    }
+
+    @Override
+    public synchronized void onOperationComplete() throws RemoteException {
+        onOperationCompleteWithStatus(OPERATION_STATUS_DEFAULT);
+    }
+
+    synchronized int getOperationStatus() {
+        if (mHasCompletedOperation) {
+            return mOperationStatus;
+        }
+
+        long timeoutLeft = mOperationTimeout;
+        try {
+            while (!mHasCompletedOperation && timeoutLeft > 0) {
+                long waitStartTime = System.currentTimeMillis();
+                wait(timeoutLeft);
+                if (mHasCompletedOperation) {
+                    return mOperationStatus;
+                }
+                timeoutLeft -= System.currentTimeMillis() - waitStartTime;
+            }
+
+            Slog.w(TAG, "Couldn't get operation status from transport");
+            return BackupTransport.TRANSPORT_ERROR;
+        } catch (InterruptedException e) {
+            Slog.w(TAG, "Couldn't get operation status from transport: ", e);
+            return BackupTransport.TRANSPORT_ERROR;
+        } finally {
+            reset();
+        }
+    }
+
+    synchronized void reset() {
+        mHasCompletedOperation = false;
+        mOperationStatus = OPERATION_STATUS_DEFAULT;
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ea345a7..2593e51 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5915,6 +5915,10 @@
             if (targetPkg == null) {
                 throw new IllegalArgumentException("null target");
             }
+            final int callingUserId = UserHandle.getUserId(r.uid);
+            if (mPackageManagerInt.filterAppAccess(targetPkg, r.uid, callingUserId)) {
+                return;
+            }
 
             Preconditions.checkFlagsArgument(modeFlags, Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@@ -5926,7 +5930,7 @@
             intent.setFlags(modeFlags);
 
             final NeededUriGrants needed = mUgmInternal.checkGrantUriPermissionFromIntent(intent,
-                    r.uid, targetPkg, UserHandle.getUserId(r.uid));
+                    r.uid, targetPkg, callingUserId);
             mUgmInternal.grantUriPermissionUncheckedFromIntent(needed, null);
         }
     }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5a54332..af4ff58 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2782,29 +2782,18 @@
     void setAttachingSchedGroupLSP(ProcessRecord app) {
         int initialSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
         final ProcessStateRecord state = app.mState;
-        // If the process has been marked as foreground via Zygote.START_FLAG_USE_TOP_APP_PRIORITY,
-        // then verify that the top priority is actually is applied.
+        // If the process has been marked as foreground, it is starting as the top app (with
+        // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
         if (state.hasForegroundActivities()) {
-            String fallbackReason = null;
             try {
                 // The priority must be the same as how does {@link #applyOomAdjLSP} set for
                 // {@link ProcessList.SCHED_GROUP_TOP_APP}. We don't check render thread because it
                 // is not ready when attaching.
-                if (Process.getProcessGroup(app.getPid()) == THREAD_GROUP_TOP_APP) {
-                    app.getWindowProcessController().onTopProcChanged();
-                    setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
-                } else {
-                    fallbackReason = "not expected top priority";
-                }
-            } catch (Exception e) {
-                fallbackReason = e.toString();
-            }
-            if (fallbackReason == null) {
+                app.getWindowProcessController().onTopProcChanged();
+                setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
                 initialSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
-            } else {
-                // The real scheduling group will depend on if there is any component of the process
-                // did something during attaching.
-                Slog.w(TAG, "Fallback pre-set sched group to default: " + fallbackReason);
+            } catch (Exception e) {
+                Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e);
             }
         }
 
diff --git a/services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java b/services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java
new file mode 100644
index 0000000..1726da2
--- /dev/null
+++ b/services/core/java/com/android/server/backup/AppSpecificLocalesBackupHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 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.server.backup;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.backup.BlobBackupHelper;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.locales.LocaleManagerInternal;
+
+/**
+ * Helper for backing up app-specific locales.
+ * <p>
+ * This helper is used in {@link com.android.server.backup.SystemBackupAgent}
+ */
+public class AppSpecificLocalesBackupHelper extends BlobBackupHelper {
+    private static final String TAG = "AppLocalesBackupHelper";   // must be < 23 chars
+    private static final boolean DEBUG = false;
+
+    // Current version of the blob schema
+    private static final int BLOB_VERSION = 1;
+
+    // Key under which the payload blob is stored
+    private static final String KEY_APP_LOCALES = "app_locales";
+
+    private final @UserIdInt int mUserId;
+
+    private final @NonNull LocaleManagerInternal mLocaleManagerInternal;
+
+    public AppSpecificLocalesBackupHelper(int userId) {
+        super(BLOB_VERSION, KEY_APP_LOCALES);
+        mUserId = userId;
+        mLocaleManagerInternal = LocalServices.getService(LocaleManagerInternal.class);
+    }
+
+    @Override
+    protected byte[] getBackupPayload(String key) {
+        if (DEBUG) {
+            Slog.d(TAG, "Handling backup of " + key);
+        }
+
+        byte[] newPayload = null;
+        if (KEY_APP_LOCALES.equals(key)) {
+            try {
+                newPayload = mLocaleManagerInternal.getBackupPayload(mUserId);
+            } catch (Exception e) {
+                // Treat as no data
+                Slog.e(TAG, "Couldn't communicate with locale manager", e);
+                newPayload = null;
+            }
+        } else {
+            Slog.w(TAG, "Unexpected backup key " + key);
+        }
+        return newPayload;
+    }
+
+    @Override
+    protected void applyRestoredPayload(String key, byte[] payload) {
+        if (DEBUG) {
+            Slog.d(TAG, "Handling restore of " + key);
+        }
+
+        if (KEY_APP_LOCALES.equals(key)) {
+            try {
+                mLocaleManagerInternal.stageAndApplyRestoredPayload(payload, mUserId);
+            } catch (Exception e) {
+                Slog.e(TAG, "Couldn't communicate with locale manager", e);
+            }
+        } else {
+            Slog.w(TAG, "Unexpected restore key " + key);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index fa18204..d39d2d1 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -57,6 +57,7 @@
     private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
     private static final String SLICES_HELPER = "slices";
     private static final String PEOPLE_HELPER = "people";
+    private static final String APP_LOCALES_HELPER = "app_locales";
 
     // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
     // are also used in the full-backup file format, so must not change unless steps are
@@ -83,7 +84,7 @@
     private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
 
     private static final Set<String> sEligibleForMultiUser = Sets.newArraySet(
-            PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER);
+            PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER);
 
     private int mUserId = UserHandle.USER_SYSTEM;
 
@@ -102,6 +103,7 @@
         addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
         addHelper(SLICES_HELPER, new SliceBackupHelper(this));
         addHelper(PEOPLE_HELPER, new PeopleBackupHelper(mUserId));
+        addHelper(APP_LOCALES_HELPER, new AppSpecificLocalesBackupHelper(mUserId));
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
new file mode 100644
index 0000000..7f7901f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/TransportStatusCallbackTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.server.backup.transport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TransportStatusCallbackTest {
+    private static final int OPERATION_TIMEOUT_MILLIS = 10;
+    private static final int OPERATION_COMPLETE_STATUS = 123;
+
+    private TransportStatusCallback mTransportStatusCallback;
+
+    @Before
+    public void setUp() {
+        mTransportStatusCallback = new TransportStatusCallback();
+    }
+
+    @Test
+    public void testGetOperationStatus_withPreCompletedOperation_returnsStatus() throws Exception {
+        mTransportStatusCallback.onOperationCompleteWithStatus(OPERATION_COMPLETE_STATUS);
+
+        int result = mTransportStatusCallback.getOperationStatus();
+
+        assertThat(result).isEqualTo(OPERATION_COMPLETE_STATUS);
+    }
+
+    @Test
+    public void testGetOperationStatus_completeOperation_returnsStatus() throws Exception {
+        Thread thread = new Thread(() -> {
+            int result = mTransportStatusCallback.getOperationStatus();
+            assertThat(result).isEqualTo(OPERATION_COMPLETE_STATUS);
+        });
+        thread.start();
+
+        mTransportStatusCallback.onOperationCompleteWithStatus(OPERATION_COMPLETE_STATUS);
+
+        thread.join();
+    }
+
+    @Test
+    public void testGetOperationStatus_operationTimesOut_returnsError() throws Exception {
+        TransportStatusCallback callback = new TransportStatusCallback(OPERATION_TIMEOUT_MILLIS);
+
+        int result = callback.getOperationStatus();
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+}