diff options
| author | Christopher Tate <ctate@google.com> | 2014-06-17 21:35:11 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-06-17 20:55:27 +0000 |
| commit | 564054146e7286b0d046591c0bd3195b0e4a6cf3 (patch) | |
| tree | 27b47ea554804bed38a0e843b8135c1b10ac9861 /core/java | |
| parent | 9cf22309e0409b3040adfc1af2e5e38eed137059 (diff) | |
| parent | 6a49dd087f29cfca82d55dfabeb97439ef84b508 (diff) | |
| download | frameworks_base-564054146e7286b0d046591c0bd3195b0e4a6cf3.zip frameworks_base-564054146e7286b0d046591c0bd3195b0e4a6cf3.tar.gz frameworks_base-564054146e7286b0d046591c0bd3195b0e4a6cf3.tar.bz2 | |
Merge "Tweak restore API"
Diffstat (limited to 'core/java')
5 files changed, 296 insertions, 52 deletions
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 56a55fb..706ef04 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -228,19 +228,35 @@ public class BackupTransport { } /** - * Get the package name of the next application with data in the backup store. + * Get the package name of the next application with data in the backup store, plus + * a description of the structure of the restored archive: either TYPE_KEY_VALUE for + * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream. * - * @return The name of one of the packages supplied to {@link #startRestore}, - * or "" (the empty string) if no more backup data is available, - * or null if an error occurred (the restore should be aborted and rescheduled). + * <p>If the package name in the returned RestoreDescription object is the singleton + * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available + * in the current restore session: all packages described in startRestore() have been + * processed. + * + * <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. */ - public String nextRestorePackage() { + public RestoreDescription nextRestorePackage() { return null; } /** - * Get the data for the application returned by {@link #nextRestorePackage}. - * @param data An open, writable file into which the backup data should be stored. + * Get the data for the application returned by {@link #nextRestorePackage}, if that + * method reported {@link RestoreDescription#TYPE_KEY_VALUE} as its delivery type. + * If the package has only TYPE_FULL_STREAM data, then this method will return an + * error. + * + * @param data An open, writable file into which the key/value backup data should be stored. * @return the same error codes as {@link #startRestore}. */ public int getRestoreData(ParcelFileDescriptor outFd) { @@ -332,32 +348,11 @@ public class BackupTransport { // Full restore interfaces /** - * Ask the transport to set up to perform a full data restore of the given packages. + * Ask the transport to provide data for the "current" package being restored. This + * is the package that was just reported by {@link #nextRestorePackage()} as having + * {@link RestoreDescription#TYPE_FULL_STREAM} data. * - * @param token A backup token as returned by {@link #getAvailableRestoreSets} - * or {@link #getCurrentRestoreSet}. - * @param targetPackage The names of the packages whose data is being requested. - * @return TRANSPORT_OK to indicate that the OS may proceed with requesting - * restore data; TRANSPORT_ERROR to indicate a fatal error condition that precludes - * performing any restore at this time. - */ - public int prepareFullRestore(long token, String[] targetPackages) { - return BackupTransport.TRANSPORT_OK; - } - - /** - * Ask the transport what package's full data will be restored next. When all apps' - * data has been delivered, the transport should return {@code null} here. - * @return The package name of the next application whose data will be restored, or - * {@code null} if all available package has been delivered. - */ - public String getNextFullRestorePackage() { - return null; - } - - /** - * Ask the transport to provide data for the "current" package being restored. The - * transport then writes some data to the socket supplied to this call, and returns + * The transport writes some data to the socket supplied to this call, and returns * the number of bytes written. The system will then read that many bytes and * stream them to the application's agent for restore, then will call this method again * to receive the next chunk of the archive. This sequence will be repeated until the @@ -369,10 +364,14 @@ public class BackupTransport { * {@link #getNextFullRestorePackage()} to begin the restore process for the next * application, and the sequence begins again. * + * <p>The transport should always close this socket when returning from this method. + * Do not cache this socket across multiple calls or you may leak file descriptors. + * * @param socket The file descriptor that the transport will use for delivering the - * streamed archive. + * 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 much data to be delivered to the app. A negative + * 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. @@ -380,6 +379,24 @@ public class BackupTransport { public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { return 0; } + + /** + * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM} + * data for restore, it will invoke this method to tell the transport that it should + * abandon the data download for the current package. The OS will then either call + * {@link #nextRestorePackage()} again to move on to restoring the next package in the + * 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. + */ + public int abortFullRestore() { + return BackupTransport.TRANSPORT_OK; + } + /** * Bridge between the actual IBackupTransport implementation and the stable API. If the * binder interface needs to change, we use this layer to translate so that we can @@ -450,7 +467,7 @@ public class BackupTransport { } @Override - public String nextRestorePackage() throws RemoteException { + public RestoreDescription nextRestorePackage() throws RemoteException { return BackupTransport.this.nextRestorePackage(); } @@ -478,5 +495,15 @@ public class BackupTransport { public int sendBackupData(int numBytes) throws RemoteException { return BackupTransport.this.sendBackupData(numBytes); } + + @Override + public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { + return BackupTransport.this.getNextFullRestoreDataChunk(socket); + } + + @Override + public int abortFullRestore() { + return BackupTransport.this.abortFullRestore(); + } } } diff --git a/core/java/android/app/backup/RestoreDescription.aidl b/core/java/android/app/backup/RestoreDescription.aidl new file mode 100644 index 0000000..9cbea78 --- /dev/null +++ b/core/java/android/app/backup/RestoreDescription.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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 RestoreDescription; diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java new file mode 100644 index 0000000..0fb4355 --- /dev/null +++ b/core/java/android/app/backup/RestoreDescription.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 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.os.Parcel; +import android.os.Parcelable; + +/** + * Description of the available restore data for a given package. Returned by a + * BackupTransport in response to a request about the next available restorable + * package. + * + * @see BackupTransport#nextRestorePackage() + * + * @hide + */ +public class RestoreDescription implements Parcelable { + private final String mPackageName; + private final int mDataType; + + private static final String NO_MORE_PACKAGES_SENTINEL = ""; + + /** + * Return this constant RestoreDescription from BackupTransport.nextRestorePackage() + * to indicate that no more package data is available in the current restore operation. + */ + public static final RestoreDescription NO_MORE_PACKAGES = + new RestoreDescription(NO_MORE_PACKAGES_SENTINEL, 0); + + // --------------------------------------- + // Data type identifiers + + /** This package's restore data is an original-style key/value dataset */ + public static final int TYPE_KEY_VALUE = 1; + + /** This package's restore data is a tarball-type full data stream */ + public static final int TYPE_FULL_STREAM = 2; + + // --------------------------------------- + // API + + public RestoreDescription(String packageName, int dataType) { + mPackageName = packageName; + mDataType = dataType; + } + + public String getPackageName() { + return mPackageName; + } + + public int getDataType() { + return mDataType; + } + + // --------------------------------------- + // Parcelable implementation - not used by transport + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(mPackageName); + out.writeInt(mDataType); + } + + public static final Parcelable.Creator<RestoreDescription> CREATOR + = new Parcelable.Creator<RestoreDescription>() { + public RestoreDescription createFromParcel(Parcel in) { + final RestoreDescription unparceled = new RestoreDescription(in); + return (NO_MORE_PACKAGES_SENTINEL.equals(unparceled.mPackageName)) + ? NO_MORE_PACKAGES + : unparceled; + } + + public RestoreDescription[] newArray(int size) { + return new RestoreDescription[size]; + } + }; + + private RestoreDescription(Parcel in) { + mPackageName = in.readString(); + mDataType = in.readInt(); + } +} diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 960fa49..643225d 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -16,6 +16,7 @@ package com.android.internal.backup; +import android.app.backup.RestoreDescription; import android.app.backup.RestoreSet; import android.content.Intent; import android.content.pm.PackageInfo; @@ -168,12 +169,25 @@ interface IBackupTransport { int startRestore(long token, in PackageInfo[] packages); /** - * Get the package name of the next application with data in the backup store. - * @return The name of one of the packages supplied to {@link #startRestore}, - * or "" (the empty string) if no more backup data is available, - * or null if an error occurred (the restore should be aborted and rescheduled). + * Get the package name of the next application with data in the backup store, plus + * a description of the structure of the restored archive: either TYPE_KEY_VALUE for + * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream. + * + * <p>If the package name in the returned RestoreDescription object is the singleton + * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available + * in the current restore session: all packages described in startRestore() have been + * processed. + * + * <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. */ - String nextRestorePackage(); + RestoreDescription nextRestorePackage(); /** * Get the data for the application returned by {@link #nextRestorePackage}. @@ -188,7 +202,58 @@ interface IBackupTransport { */ void finishRestore(); + // full backup stuff + long requestFullBackupTime(); int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket); int sendBackupData(int numBytes); + + // full restore stuff + + /** + * Ask the transport to provide data for the "current" package being restored. This + * is the package that was just reported by {@link #nextRestorePackage()} as having + * {@link RestoreDescription#TYPE_FULL_STREAM} data. + * + * The transport writes some data to the socket supplied to this call, and returns + * the number of bytes written. The system will then read that many bytes and + * stream them to the application's agent for restore, then will call this method again + * to receive the next chunk of the archive. This sequence will be repeated until the + * transport returns zero indicating that all of the package's data has been delivered + * (or returns a negative value indicating some sort of hard error condition at the + * transport level). + * + * <p>After this method returns zero, the system will then call + * {@link #getNextFullRestorePackage()} to begin the restore process for the next + * application, and the sequence begins again. + * + * <p>The transport should always close this socket when returning from this method. + * Do not cache this socket across multiple calls or you may leak file descriptors. + * + * @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. + */ + int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket); + + /** + * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM} + * data for restore, it will invoke this method to tell the transport that it should + * abandon the data download for the current package. The OS will then either call + * {@link #nextRestorePackage()} again to move on to restoring the next package in the + * 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. + */ + int abortFullRestore(); + } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index c9d621d..ff0ee65 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -19,6 +19,7 @@ package com.android.internal.backup; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupTransport; +import android.app.backup.RestoreDescription; import android.app.backup.RestoreSet; import android.content.ComponentName; import android.content.Context; @@ -63,18 +64,24 @@ public class LocalTransport extends BackupTransport { private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; + private static final String INCREMENTAL_DIR = "_delta"; + private static final String FULL_DATA_DIR = "_full"; + // The currently-active restore set always has the same (nonzero!) token private static final long CURRENT_SET_TOKEN = 1; private Context mContext; private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN)); - private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, "_delta"); - private File mCurrentSetFullDir = new File(mCurrentSetDir, "_full"); + private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR); + private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR); private PackageInfo[] mRestorePackages = null; private int mRestorePackage = -1; // Index into mRestorePackages - private File mRestoreDataDir; + private int mRestoreType; + private File mRestoreSetDir; + private File mRestoreSetIncrementalDir; + private File mRestoreSetFullDir; private long mRestoreToken; // Additional bookkeeping for full backup @@ -361,30 +368,55 @@ public class LocalTransport extends BackupTransport { mRestorePackages = packages; mRestorePackage = -1; mRestoreToken = token; - mRestoreDataDir = new File(mDataDir, Long.toString(token)); + mRestoreSetDir = new File(mDataDir, Long.toString(token)); + mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR); + mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR); return BackupTransport.TRANSPORT_OK; } - public String nextRestorePackage() { + @Override + public RestoreDescription nextRestorePackage() { if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); + + boolean found = false; while (++mRestorePackage < mRestorePackages.length) { String name = mRestorePackages[mRestorePackage].packageName; + + // If we have key/value data for this package, deliver that // skip packages where we have a data dir but no actual contents - String[] contents = (new File(mRestoreDataDir, name)).list(); + String[] contents = (new File(mRestoreSetIncrementalDir, name)).list(); if (contents != null && contents.length > 0) { - if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); - return name; + if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) = " + name); + mRestoreType = RestoreDescription.TYPE_KEY_VALUE; + found = true; + } + + if (!found) { + // No key/value data; check for [non-empty] full data + File maybeFullData = new File(mRestoreSetFullDir, name); + if (maybeFullData.length() > 0) { + if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_FULL_STREAM) = " + name); + mRestoreType = RestoreDescription.TYPE_FULL_STREAM; + found = true; + } + } + + if (found) { + return new RestoreDescription(name, mRestoreType); } } if (DEBUG) Log.v(TAG, " no more packages to restore"); - return ""; + return RestoreDescription.NO_MORE_PACKAGES; } public int getRestoreData(ParcelFileDescriptor outFd) { if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); - File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName); + if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) { + throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset"); + } + File packageDir = new File(mRestoreSetDir, mRestorePackages[mRestorePackage].packageName); // The restore set is the concatenation of the individual record blobs, // each of which is a file in the package's directory. We return the @@ -463,8 +495,8 @@ public class LocalTransport extends BackupTransport { // Full restore handling public int prepareFullRestore(long token, String[] targetPackages) { - mRestoreDataDir = new File(mDataDir, Long.toString(token)); - mFullRestoreSetDir = new File(mRestoreDataDir, "_full"); + mRestoreSetDir = new File(mDataDir, Long.toString(token)); + mFullRestoreSetDir = new File(mRestoreSetDir, FULL_DATA_DIR); mFullRestorePackages = new HashSet<String>(); if (mFullRestoreSetDir.exists()) { List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list()); |
