Merge changes from topic "async-transport"
* changes: Update BackupTransportCallback to use async AIDL Make IBackupTransport AIDL async
This commit is contained in:
committed by
Android (Google) Code Review
commit
4c44a2f18a
@@ -25,6 +25,11 @@ import android.os.ParcelFileDescriptor;
|
||||
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 @@ public class BackupTransport {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,39 +22,46 @@ import android.content.Intent;
|
||||
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 @@ interface IBackupTransport {
|
||||
* <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 @@ interface IBackupTransport {
|
||||
* 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 @@ interface IBackupTransport {
|
||||
* <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 @@ interface IBackupTransport {
|
||||
* 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 @@ interface IBackupTransport {
|
||||
* 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 @@ interface IBackupTransport {
|
||||
* 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 @@ interface IBackupTransport {
|
||||
* 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 @@ interface IBackupTransport {
|
||||
* 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 @@ interface IBackupTransport {
|
||||
* <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 @@ interface IBackupTransport {
|
||||
* @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 @@ interface IBackupTransport {
|
||||
* @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 @@ interface IBackupTransport {
|
||||
* @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 @@ interface IBackupTransport {
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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.content.pm.PackageInfo;
|
||||
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 @@ public class BackupTransportClient {
|
||||
*/
|
||||
@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 class BackupTransportClient {
|
||||
*/
|
||||
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 class BackupTransportClient {
|
||||
*/
|
||||
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 class BackupTransportClient {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user