Loading core/java/android/app/backup/BackupTransport.java +62 −35 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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. Loading @@ -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 Loading Loading @@ -450,7 +467,7 @@ public class BackupTransport { } @Override public String nextRestorePackage() throws RemoteException { public RestoreDescription nextRestorePackage() throws RemoteException { return BackupTransport.this.nextRestorePackage(); } Loading Loading @@ -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(); } } } core/java/android/app/backup/RestoreDescription.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * 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; core/java/android/app/backup/RestoreDescription.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * 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(); } } core/java/com/android/internal/backup/IBackupTransport.aidl +70 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}. Loading @@ -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(); } core/java/com/android/internal/backup/LocalTransport.java +44 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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()); Loading Loading
core/java/android/app/backup/BackupTransport.java +62 −35 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 Loading @@ -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. Loading @@ -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 Loading Loading @@ -450,7 +467,7 @@ public class BackupTransport { } @Override public String nextRestorePackage() throws RemoteException { public RestoreDescription nextRestorePackage() throws RemoteException { return BackupTransport.this.nextRestorePackage(); } Loading Loading @@ -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(); } } }
core/java/android/app/backup/RestoreDescription.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * 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;
core/java/android/app/backup/RestoreDescription.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * 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(); } }
core/java/com/android/internal/backup/IBackupTransport.aidl +70 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}. Loading @@ -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(); }
core/java/com/android/internal/backup/LocalTransport.java +44 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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()); Loading