diff options
| author | Sergey Poromov <poromov@google.com> | 2015-12-15 16:26:23 +0100 |
|---|---|---|
| committer | Sergey Poromov <poromov@google.com> | 2016-01-21 19:15:33 +0100 |
| commit | fe06bf64d204c459699b0bf6465f9fb69208345e (patch) | |
| tree | 9c17b7068072e9117c1b665db5d02f6b4cb17097 /core/java/android | |
| parent | b325061b196e8bb849356c1beb09de9d18bbe97b (diff) | |
Introduce BackupManager#requestBackup & BackupObserver API
Introduces a way to request immediate backup for list of packages
and receive callbacks on backup progress.
Bug: 25688526
Change-Id: Ib826933d44f4ebf2b981f8be366215b2d37847e2
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/backup/BackupManager.java | 155 | ||||
| -rw-r--r-- | core/java/android/app/backup/BackupObserver.java | 59 | ||||
| -rw-r--r-- | core/java/android/app/backup/BackupProgress.aidl | 19 | ||||
| -rw-r--r-- | core/java/android/app/backup/BackupProgress.java | 69 | ||||
| -rw-r--r-- | core/java/android/app/backup/BackupTransport.java | 38 | ||||
| -rw-r--r-- | core/java/android/app/backup/IBackupManager.aidl | 16 | ||||
| -rw-r--r-- | core/java/android/app/backup/IBackupObserver.aidl | 55 | ||||
| -rw-r--r-- | core/java/android/app/backup/RestoreSet.java | 1 |
8 files changed, 399 insertions, 13 deletions
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 8b7930590126..193a0b2c768d 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -17,13 +17,13 @@ package android.app.backup; import android.annotation.SystemApi; -import android.app.backup.RestoreSession; -import android.app.backup.IBackupManager; -import android.app.backup.IRestoreSession; import android.content.Context; +import android.os.Handler; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.Pair; /** * The interface through which an application interacts with the Android backup service to @@ -59,6 +59,65 @@ import android.util.Log; public class BackupManager { private static final String TAG = "BackupManager"; + // BackupObserver status codes + /** + * Indicates that backup succeeded. + * + * @hide + */ + @SystemApi + public static final int SUCCESS = 0; + + /** + * Indicates that backup is either not enabled at all or + * backup for the package was rejected by backup service + * or backup transport, + * + * @hide + */ + @SystemApi + public static final int ERROR_BACKUP_NOT_ALLOWED = -2001; + + /** + * The requested app is not installed on the device. + * + * @hide + */ + @SystemApi + public static final int ERROR_PACKAGE_NOT_FOUND = -2002; + + /** + * The transport for some reason was not in a good state and + * aborted the entire backup request. This is a transient + * failure and should not be retried immediately. + * + * @hide + */ + @SystemApi + public static final int ERROR_TRANSPORT_ABORTED = BackupTransport.TRANSPORT_ERROR; + + /** + * Returned when the transport was unable to process the + * backup request for a given package, for example if the + * transport hit a transient network failure. The remaining + * packages provided to {@link #requestBackup(String[], BackupObserver)} + * will still be attempted. + * + * @hide + */ + @SystemApi + public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = + BackupTransport.TRANSPORT_PACKAGE_REJECTED; + + /** + * The {@link BackupAgent} for the requested package failed for some reason + * and didn't provide appropriate backup data. + * + * @hide + */ + @SystemApi + public static final int ERROR_AGENT_FAILURE = BackupTransport.AGENT_ERROR; + private Context mContext; private static IBackupManager sService; @@ -365,4 +424,94 @@ public class BackupManager { } return 0; } + + /** + * Request an immediate backup, providing an observer to which results of the backup operation + * will be published. The Android backup system will decide for each package whether it will + * be full app data backup or key/value-pair-based backup. + * + * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all + * provided packages using the remote transport. + * + * @param packages List of package names to backup. + * @param observer The {@link BackupObserver} to receive callbacks during the backup + * operation. + * @return {@link BackupManager#SUCCESS} on success; nonzero on error. + * @exception IllegalArgumentException on null or empty {@code packages} param. + * + * @hide + */ + @SystemApi + public int requestBackup(String[] packages, BackupObserver observer) { + checkServiceBinder(); + if (sService != null) { + try { + BackupObserverWrapper observerWrapper = + new BackupObserverWrapper(mContext, observer); + return sService.requestBackup(packages, observerWrapper); + } catch (RemoteException e) { + Log.e(TAG, "requestBackup() couldn't connect"); + } + } + return -1; + } + + /* + * We wrap incoming binder calls with a private class implementation that + * redirects them into main-thread actions. This serializes the backup + * progress callbacks nicely within the usual main-thread lifecycle pattern. + */ + @SystemApi + private class BackupObserverWrapper extends IBackupObserver.Stub { + final Handler mHandler; + final BackupObserver mObserver; + + static final int MSG_UPDATE = 1; + static final int MSG_RESULT = 2; + static final int MSG_FINISHED = 3; + + BackupObserverWrapper(Context context, BackupObserver observer) { + mHandler = new Handler(context.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE: + Pair<String, BackupProgress> obj = + (Pair<String, BackupProgress>) msg.obj; + mObserver.onUpdate(obj.first, obj.second); + break; + case MSG_RESULT: + mObserver.onResult((String)msg.obj, msg.arg1); + break; + case MSG_FINISHED: + mObserver.backupFinished(msg.arg1); + break; + default: + Log.w(TAG, "Unknown message: " + msg); + break; + } + } + }; + mObserver = observer; + } + + // Binder calls into this object just enqueue on the main-thread handler + @Override + public void onUpdate(String currentPackage, BackupProgress backupProgress) { + mHandler.sendMessage( + mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress))); + } + + @Override + public void onResult(String currentPackage, int status) { + mHandler.sendMessage( + mHandler.obtainMessage(MSG_FINISHED, status, 0, currentPackage)); + } + + @Override + public void backupFinished(int status) { + mHandler.sendMessage( + mHandler.obtainMessage(MSG_FINISHED, status, 0)); + } + } } diff --git a/core/java/android/app/backup/BackupObserver.java b/core/java/android/app/backup/BackupObserver.java new file mode 100644 index 000000000000..0dd071e9cc99 --- /dev/null +++ b/core/java/android/app/backup/BackupObserver.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 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 android.app.backup; + +import android.annotation.SystemApi; + +/** + * Callback class for receiving progress reports during a backup operation. These + * methods will all be called on your application's main thread. + * + * @hide + */ +@SystemApi +public abstract class BackupObserver { + /** + * This method could be called several times for packages with full data backup. + * It will tell how much of backup data is already saved and how much is expected. + * + * @param currentBackupPackage The name of the package that now being backuped. + * @param backupProgress Current progress of backup for the package. + */ + public void onUpdate(String currentBackupPackage, BackupProgress backupProgress) { + } + + /** + * The backup of single package has completed. This method will be called at most one time + * for each package and could be not called if backup is failed before and + * backupFinished() is called. + * + * @param currentBackupPackage The name of the package that was backuped. + * @param status Zero on success; a nonzero error code if the backup operation failed. + */ + public void onResult(String currentBackupPackage, int status) { + } + + /** + * The backup process has completed. This method will always be called, + * even if no individual package backup operations were attempted. + * + * @param status Zero on success; a nonzero error code if the backup operation + * as a whole failed. + */ + public void backupFinished(int status) { + } +} diff --git a/core/java/android/app/backup/BackupProgress.aidl b/core/java/android/app/backup/BackupProgress.aidl new file mode 100644 index 000000000000..c10b9a20f933 --- /dev/null +++ b/core/java/android/app/backup/BackupProgress.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016 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 android.app.backup; + +parcelable BackupProgress;
\ No newline at end of file diff --git a/core/java/android/app/backup/BackupProgress.java b/core/java/android/app/backup/BackupProgress.java new file mode 100644 index 000000000000..32e62126a6db --- /dev/null +++ b/core/java/android/app/backup/BackupProgress.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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 android.app.backup; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Information about current progress of full data backup + * Used in {@link BackupObserver#onUpdate(String, BackupProgress)} + * + * @hide + */ +@SystemApi +public class BackupProgress implements Parcelable { + + /** + * Expected size of data in full backup. + */ + public final long bytesExpected; + /** + * Amount of backup data that is already saved in backup. + */ + public final long bytesTransferred; + + public BackupProgress(long _bytesExpected, long _bytesTransferred) { + bytesExpected = _bytesExpected; + bytesTransferred = _bytesTransferred; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(bytesExpected); + out.writeLong(bytesTransferred); + } + + public static final Creator<BackupProgress> CREATOR = new Creator<BackupProgress>() { + public BackupProgress createFromParcel(Parcel in) { + return new BackupProgress(in); + } + + public BackupProgress[] newArray(int size) { + return new BackupProgress[size]; + } + }; + + private BackupProgress(Parcel in) { + bytesExpected = in.readLong(); + bytesTransferred = in.readLong(); + } +} diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 954ccef93b44..4363604cba92 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -50,6 +50,10 @@ public class BackupTransport { public static final int AGENT_ERROR = -1003; public static final int AGENT_UNKNOWN = -1004; + // Indicates that operation was initiated by user, not a scheduled one. + // Transport should ignore its own moratoriums for call with this flag set. + public static final int FLAG_USER_INITIATED = 1; + IBackupTransport mBinderImpl = new TransportImpl(); public IBinder getBinder() { @@ -228,13 +232,10 @@ public class BackupTransport { * * @param packageInfo The identity of the application whose data is being backed up. * This specifically includes the signature list for the package. - * @param data The data stream that resulted from invoking the application's + * @param inFd Descriptor of file with data that resulted from invoking the application's * BackupService.doBackup() method. This may be a pipe rather than a file on * persistent media, so it may not be seekable. - * @param wipeAllFirst When true, <i>all</i> backed-up data for the current device/account - * must be erased prior to the storage of the data provided here. The purpose of this - * is to provide a guarantee that no stale data exists in the restore set when the - * device begins providing incremental backups. + * @param flags {@link BackupTransport#FLAG_USER_INITIATED} or 0. * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far), * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} (to suppress backup of this * specific package, but allow others to proceed), @@ -242,6 +243,14 @@ public class BackupTransport { * {@link BackupTransport#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has * become lost due to inactivity purge or some other reason and needs re-initializing) */ + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) { + return performBackup(packageInfo, inFd); + } + + /** + * Legacy version of {@link #performBackup(PackageInfo, ParcelFileDescriptor, int)} that + * doesn't use flags parameter. + */ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) { return BackupTransport.TRANSPORT_ERROR; } @@ -392,11 +401,21 @@ public class BackupTransport { * close this file descriptor now; otherwise it should be cached for use during * succeeding calls to {@link #sendBackupData(int)}, and closed in response to * {@link #finishBackup()}. + * @param flags {@link BackupTransport#FLAG_USER_INITIATED} or 0. * @return TRANSPORT_PACKAGE_REJECTED to indicate that the stated application is not * to be backed up; TRANSPORT_OK to indicate that the OS may proceed with delivering * backup data; TRANSPORT_ERROR to indicate a fatal error condition that precludes * performing a backup at this time. */ + public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, + int flags) { + return performFullBackup(targetPackage, socket); + } + + /** + * Legacy version of {@link #performFullBackup(PackageInfo, ParcelFileDescriptor, int)} that + * doesn't use flags parameter. + */ public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) { return BackupTransport.TRANSPORT_PACKAGE_REJECTED; } @@ -568,9 +587,9 @@ public class BackupTransport { } @Override - public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags) throws RemoteException { - return BackupTransport.this.performBackup(packageInfo, inFd); + return BackupTransport.this.performBackup(packageInfo, inFd, flags); } @Override @@ -619,8 +638,9 @@ public class BackupTransport { } @Override - public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) throws RemoteException { - return BackupTransport.this.performFullBackup(targetPackage, socket); + public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket, + int flags) throws RemoteException { + return BackupTransport.this.performFullBackup(targetPackage, socket, flags); } @Override diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 87e4ef120730..2a1c00f9adc4 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -16,6 +16,7 @@ package android.app.backup; +import android.app.backup.IBackupObserver; import android.app.backup.IFullBackupRestoreObserver; import android.app.backup.IRestoreSession; import android.os.ParcelFileDescriptor; @@ -326,4 +327,19 @@ interface IBackupManager { * no suitable data is available. */ long getAvailableRestoreToken(String packageName); + + /** + * Request an immediate backup, providing an observer to which results of the backup operation + * will be published. The Android backup system will decide for each package whether it will + * be full app data backup or key/value-pair-based backup. + * + * <p>If this method returns zero (meaning success), the OS will attempt to backup all provided + * packages using the remote transport. + * + * @param observer The {@link BackupObserver} to receive callbacks during the backup + * operation. + * + * @return Zero on success; nonzero on error. + */ + int requestBackup(in String[] packages, IBackupObserver observer); } diff --git a/core/java/android/app/backup/IBackupObserver.aidl b/core/java/android/app/backup/IBackupObserver.aidl new file mode 100644 index 000000000000..821a58946311 --- /dev/null +++ b/core/java/android/app/backup/IBackupObserver.aidl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 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 android.app.backup; + +import android.app.backup.BackupProgress; + +/** + * Callback class for receiving progress reports during a backup operation. These + * methods will all be called on your application's main thread. + * + * @hide + */ +oneway interface IBackupObserver { + /** + * This method could be called several times for packages with full data backup. + * It will tell how much of backup data is already saved and how much is expected. + * + * @param currentBackupPackage The name of the package that now being backuped. + * @param backupProgress Current progress of backup for the package. + */ + void onUpdate(String currentPackage, in BackupProgress backupProgress); + + /** + * The backup of single package has completed. This method will be called at most one time + * for each package and could be not called if backup is failed before and + * backupFinished() is called. + * + * @param currentBackupPackage The name of the package that was backuped. + * @param status Zero on success; a nonzero error code if the backup operation failed. + */ + void onResult(String currentPackage, int status); + + /** + * The backup process has completed. This method will always be called, + * even if no individual package backup operations were attempted. + * + * @param status Zero on success; a nonzero error code if the backup operation + * as a whole failed. + */ + void backupFinished(int status); +} diff --git a/core/java/android/app/backup/RestoreSet.java b/core/java/android/app/backup/RestoreSet.java index aacaf7cda50b..4a6316c58715 100644 --- a/core/java/android/app/backup/RestoreSet.java +++ b/core/java/android/app/backup/RestoreSet.java @@ -58,7 +58,6 @@ public class RestoreSet implements Parcelable { token = _token; } - // Parcelable implementation public int describeContents() { return 0; |
