Full backup/restore now handles OBBs sensibly
OBB backup/ restore is no longer handled within the target app process. This is done to avoid having to require that OBB-using apps have full read/write permission for external storage. The new OBB backup service is a new component running in the same app as the already-existing shared storage backup agent. The backup infrastructure delegates backup/restore of apps' OBB contents to this component (because the system process may not itself read/write external storage). From the command line, OBB backup is enabled by using new -obb / -noobb flags with adb backup. The default is noobb. Finally, a couple of nit fixes: - buffer-size mismatch between the writer and reader of chunked file data has been corrected; now the reading side won't be issuing an extra pipe read per chunk. - bu now explicitly closes the transport socket fd after adopting it. This was benign but triggered a logged warning about leaked fds. Bug: 6718844 Change-Id: Ie252494e2327e9ab97cf9ed87c298410a8618492
This commit is contained in:
@@ -180,6 +180,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
|
||||
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
|
||||
core/java/com/android/internal/backup/IBackupTransport.aidl \
|
||||
core/java/com/android/internal/backup/IObbBackupService.aidl \
|
||||
core/java/com/android/internal/policy/IFaceLockCallback.aidl \
|
||||
core/java/com/android/internal/policy/IFaceLockInterface.aidl \
|
||||
core/java/com/android/internal/policy/IKeyguardShowCallback.aidl \
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class Backup {
|
||||
@@ -64,6 +65,7 @@ public final class Backup {
|
||||
private void doFullBackup(int socketFd) {
|
||||
ArrayList<String> packages = new ArrayList<String>();
|
||||
boolean saveApks = false;
|
||||
boolean saveObbs = false;
|
||||
boolean saveShared = false;
|
||||
boolean doEverything = false;
|
||||
boolean allIncludesSystem = true;
|
||||
@@ -75,6 +77,10 @@ public final class Backup {
|
||||
saveApks = true;
|
||||
} else if ("-noapk".equals(arg)) {
|
||||
saveApks = false;
|
||||
} else if ("-obb".equals(arg)) {
|
||||
saveObbs = true;
|
||||
} else if ("-noobb".equals(arg)) {
|
||||
saveObbs = false;
|
||||
} else if ("-shared".equals(arg)) {
|
||||
saveShared = true;
|
||||
} else if ("-noshared".equals(arg)) {
|
||||
@@ -104,23 +110,37 @@ public final class Backup {
|
||||
return;
|
||||
}
|
||||
|
||||
ParcelFileDescriptor fd = null;
|
||||
try {
|
||||
ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
|
||||
fd = ParcelFileDescriptor.adoptFd(socketFd);
|
||||
String[] packArray = new String[packages.size()];
|
||||
mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything, allIncludesSystem,
|
||||
packages.toArray(packArray));
|
||||
mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doEverything,
|
||||
allIncludesSystem, packages.toArray(packArray));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to invoke backup manager for backup");
|
||||
} finally {
|
||||
if (fd != null) {
|
||||
try {
|
||||
fd.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doFullRestore(int socketFd) {
|
||||
// No arguments to restore
|
||||
ParcelFileDescriptor fd = null;
|
||||
try {
|
||||
ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
|
||||
fd = ParcelFileDescriptor.adoptFd(socketFd);
|
||||
mBackupManager.fullRestore(fd);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to invoke backup manager for restore");
|
||||
} finally {
|
||||
if (fd != null) {
|
||||
try {
|
||||
fd.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -472,6 +472,7 @@ public abstract class BackupAgent extends ContextWrapper {
|
||||
File efLocation = getExternalFilesDir(null);
|
||||
if (efLocation != null) {
|
||||
basePath = getExternalFilesDir(null).getCanonicalPath();
|
||||
mode = -1; // < 0 is a token to skip attempting a chmod()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -89,7 +89,7 @@ public class FullBackup {
|
||||
* last modification time of the output file. if the {@code mode} parameter is
|
||||
* negative then this parameter will be ignored.
|
||||
* @param outFile Location within the filesystem to place the data. This must point
|
||||
* to a location that is writeable by the caller, prefereably using an absolute path.
|
||||
* to a location that is writeable by the caller, preferably using an absolute path.
|
||||
* @throws IOException
|
||||
*/
|
||||
static public void restoreFile(ParcelFileDescriptor data,
|
||||
|
||||
@@ -152,6 +152,8 @@ interface IBackupManager {
|
||||
* @param fd The file descriptor to which a 'tar' file stream is to be written
|
||||
* @param includeApks If <code>true</code>, the resulting tar stream will include the
|
||||
* application .apk files themselves as well as their data.
|
||||
* @param includeObbs If <code>true</code>, the resulting tar stream will include any
|
||||
* application expansion (OBB) files themselves belonging to each application.
|
||||
* @param includeShared If <code>true</code>, the resulting tar stream will include
|
||||
* the contents of the device's shared storage (SD card or equivalent).
|
||||
* @param allApps If <code>true</code>, the resulting tar stream will include all
|
||||
@@ -164,8 +166,9 @@ interface IBackupManager {
|
||||
* @param packageNames The package names of the apps whose data (and optionally .apk files)
|
||||
* are to be backed up. The <code>allApps</code> parameter supersedes this.
|
||||
*/
|
||||
void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
|
||||
boolean allApps, boolean allIncludesSystem, in String[] packageNames);
|
||||
void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
|
||||
boolean includeShared, boolean allApps, boolean allIncludesSystem,
|
||||
in String[] packageNames);
|
||||
|
||||
/**
|
||||
* Restore device content from the data stream passed through the given socket. The
|
||||
|
||||
45
core/java/com/android/internal/backup/IObbBackupService.aidl
Normal file
45
core/java/com/android/internal/backup/IObbBackupService.aidl
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013, 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;
|
||||
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
/**
|
||||
* Interface for the Backup Manager Service to communicate with a helper service that
|
||||
* handles local (whole-file) backup & restore of OBB content on behalf of applications.
|
||||
* This can't be done within the Backup Manager Service itself because of the restrictions
|
||||
* on system-user access to external storage, and can't be left to the apps because even
|
||||
* apps that do not have permission to access external storage in the usual way can still
|
||||
* use OBBs.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IObbBackupService {
|
||||
/*
|
||||
* Back up a package's OBB directory tree
|
||||
*/
|
||||
void backupObbs(in String packageName, in ParcelFileDescriptor data,
|
||||
int token, in IBackupManager callbackBinder);
|
||||
|
||||
/*
|
||||
* Restore an OBB file for the given package from the incoming stream
|
||||
*/
|
||||
void restoreObbFile(in String pkgName, in ParcelFileDescriptor data,
|
||||
long fileSize, int type, in String path, long mode, long mtime,
|
||||
int token, in IBackupManager callbackBinder);
|
||||
}
|
||||
@@ -553,7 +553,7 @@ int write_tarfile(const String8& packageName, const String8& domain,
|
||||
if (buf == NULL) {
|
||||
ALOGE("Out of mem allocating transfer buffer");
|
||||
err = ENOMEM;
|
||||
goto cleanup;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Magic fields for the ustar file format
|
||||
|
||||
@@ -24,5 +24,12 @@
|
||||
<application android:allowClearUserData="false"
|
||||
android:permission="android.permission.CONFIRM_FULL_BACKUP"
|
||||
android:backupAgent="SharedStorageAgent" >
|
||||
|
||||
<service android:name=".ObbBackupService"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
-keep class com.android.sharedstoragebackup.SharedStorageAgent
|
||||
-keep class com.android.sharedstoragebackup.ObbBackupService
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.sharedstoragebackup;
|
||||
|
||||
import android.app.Service;
|
||||
import android.app.backup.BackupDataOutput;
|
||||
import android.app.backup.FullBackup;
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.android.internal.backup.IObbBackupService;
|
||||
|
||||
/**
|
||||
* Service that the Backup Manager Services delegates OBB backup/restore operations to,
|
||||
* because those require accessing external storage.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public class ObbBackupService extends Service {
|
||||
static final String TAG = "ObbBackupService";
|
||||
static final boolean DEBUG = true;
|
||||
|
||||
/**
|
||||
* IObbBackupService interface implementation
|
||||
*/
|
||||
IObbBackupService mService = new IObbBackupService.Stub() {
|
||||
/*
|
||||
* Back up a package's OBB directory tree
|
||||
*/
|
||||
@Override
|
||||
public void backupObbs(String packageName, ParcelFileDescriptor data,
|
||||
int token, IBackupManager callbackBinder) {
|
||||
final FileDescriptor outFd = data.getFileDescriptor();
|
||||
try {
|
||||
File obbDir = Environment.getExternalStorageAppObbDirectory(packageName);
|
||||
if (obbDir != null) {
|
||||
if (obbDir.exists()) {
|
||||
ArrayList<File> obbList = allFileContents(obbDir);
|
||||
if (obbList != null) {
|
||||
// okay, there's at least something there
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, obbList.size() + " files to back up");
|
||||
}
|
||||
final String rootPath = obbDir.getCanonicalPath();
|
||||
final BackupDataOutput out = new BackupDataOutput(outFd);
|
||||
for (File f : obbList) {
|
||||
final String filePath = f.getCanonicalPath();
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "storing: " + filePath);
|
||||
}
|
||||
FullBackup.backupToTar(packageName, FullBackup.OBB_TREE_TOKEN, null,
|
||||
rootPath, filePath, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Exception backing up OBBs for " + packageName, e);
|
||||
} finally {
|
||||
// Send the EOD marker indicating that there is no more data
|
||||
try {
|
||||
FileOutputStream out = new FileOutputStream(outFd);
|
||||
byte[] buf = new byte[4];
|
||||
out.write(buf);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to finalize obb backup stream!");
|
||||
}
|
||||
|
||||
try {
|
||||
callbackBinder.opComplete(token);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore an OBB file for the given package from the incoming stream
|
||||
*/
|
||||
@Override
|
||||
public void restoreObbFile(String packageName, ParcelFileDescriptor data,
|
||||
long fileSize, int type, String path, long mode, long mtime,
|
||||
int token, IBackupManager callbackBinder) {
|
||||
try {
|
||||
File outFile = Environment.getExternalStorageAppObbDirectory(packageName);
|
||||
if (outFile != null) {
|
||||
outFile = new File(outFile, path);
|
||||
}
|
||||
// outFile is null here if we couldn't get access to external storage,
|
||||
// in which case restoreFile() will discard the data cleanly and let
|
||||
// us proceed with the next file segment in the stream. We pass -1
|
||||
// for the file mode to suppress attempts to chmod() on shared storage.
|
||||
FullBackup.restoreFile(data, fileSize, type, -1, mtime, outFile);
|
||||
} catch (IOException e) {
|
||||
Log.i(TAG, "Exception restoring OBB " + path, e);
|
||||
} finally {
|
||||
try {
|
||||
callbackBinder.opComplete(token);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<File> allFileContents(File rootDir) {
|
||||
final ArrayList<File> files = new ArrayList<File>();
|
||||
final ArrayList<File> dirs = new ArrayList<File>();
|
||||
|
||||
dirs.add(rootDir);
|
||||
while (!dirs.isEmpty()) {
|
||||
File dir = dirs.remove(0);
|
||||
File[] contents = dir.listFiles();
|
||||
if (contents != null) {
|
||||
for (File f : contents) {
|
||||
if (f.isDirectory()) dirs.add(f);
|
||||
else if (f.isFile()) files.add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mService.asBinder();
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ import android.util.StringBuilderPrinter;
|
||||
|
||||
import com.android.internal.backup.BackupConstants;
|
||||
import com.android.internal.backup.IBackupTransport;
|
||||
import com.android.internal.backup.IObbBackupService;
|
||||
import com.android.internal.backup.LocalTransport;
|
||||
import com.android.server.PackageManagerBackupAgent.Metadata;
|
||||
|
||||
@@ -363,15 +364,17 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
|
||||
class FullBackupParams extends FullParams {
|
||||
public boolean includeApks;
|
||||
public boolean includeObbs;
|
||||
public boolean includeShared;
|
||||
public boolean allApps;
|
||||
public boolean includeSystem;
|
||||
public String[] packages;
|
||||
|
||||
FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
|
||||
boolean doAllApps, boolean doSystem, String[] pkgList) {
|
||||
FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
|
||||
boolean saveShared, boolean doAllApps, boolean doSystem, String[] pkgList) {
|
||||
fd = output;
|
||||
includeApks = saveApks;
|
||||
includeObbs = saveObbs;
|
||||
includeShared = saveShared;
|
||||
allApps = doAllApps;
|
||||
includeSystem = doSystem;
|
||||
@@ -550,7 +553,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
// similar to normal backup/restore.
|
||||
FullBackupParams params = (FullBackupParams)msg.obj;
|
||||
PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
|
||||
params.observer, params.includeApks,
|
||||
params.observer, params.includeApks, params.includeObbs,
|
||||
params.includeShared, params.curPassword, params.encryptPassword,
|
||||
params.allApps, params.includeSystem, params.packages, params.latch);
|
||||
(new Thread(task)).start();
|
||||
@@ -2306,13 +2309,132 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
}
|
||||
|
||||
|
||||
// ----- Full backup to a file/socket -----
|
||||
// ----- Full backup/restore to a file/socket -----
|
||||
|
||||
class PerformFullBackupTask implements Runnable {
|
||||
abstract class ObbServiceClient {
|
||||
public IObbBackupService mObbService;
|
||||
public void setObbBinder(IObbBackupService binder) {
|
||||
mObbService = binder;
|
||||
}
|
||||
}
|
||||
|
||||
class FullBackupObbConnection implements ServiceConnection {
|
||||
volatile IObbBackupService mService;
|
||||
|
||||
FullBackupObbConnection() {
|
||||
mService = null;
|
||||
}
|
||||
|
||||
public void establish() {
|
||||
if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
|
||||
Intent obbIntent = new Intent().setComponent(new ComponentName(
|
||||
"com.android.sharedstoragebackup",
|
||||
"com.android.sharedstoragebackup.ObbBackupService"));
|
||||
BackupManagerService.this.mContext.bindService(
|
||||
obbIntent, this, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
public void tearDown() {
|
||||
BackupManagerService.this.mContext.unbindService(this);
|
||||
}
|
||||
|
||||
public boolean backupObbs(PackageInfo pkg, OutputStream out) {
|
||||
boolean success = false;
|
||||
waitForConnection();
|
||||
|
||||
ParcelFileDescriptor[] pipes = null;
|
||||
try {
|
||||
pipes = ParcelFileDescriptor.createPipe();
|
||||
int token = generateToken();
|
||||
prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
|
||||
mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
|
||||
routeSocketDataToOutput(pipes[0], out);
|
||||
success = waitUntilOperationComplete(token);
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
|
||||
} finally {
|
||||
try {
|
||||
out.flush();
|
||||
if (pipes != null) {
|
||||
if (pipes[0] != null) pipes[0].close();
|
||||
if (pipes[1] != null) pipes[1].close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.w(TAG, "I/O error closing down OBB backup", e);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
|
||||
long fileSize, int type, String path, long mode, long mtime,
|
||||
int token, IBackupManager callbackBinder) {
|
||||
waitForConnection();
|
||||
|
||||
try {
|
||||
mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
|
||||
token, callbackBinder);
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForConnection() {
|
||||
synchronized (this) {
|
||||
while (mService == null) {
|
||||
if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) { /* never interrupted */ }
|
||||
}
|
||||
if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
synchronized (this) {
|
||||
mService = IObbBackupService.Stub.asInterface(service);
|
||||
if (DEBUG) Slog.i(TAG, "OBB service connection " + mService
|
||||
+ " connected on " + this);
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
synchronized (this) {
|
||||
mService = null;
|
||||
if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
|
||||
throws IOException {
|
||||
FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
|
||||
DataInputStream in = new DataInputStream(raw);
|
||||
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
int chunkTotal;
|
||||
while ((chunkTotal = in.readInt()) > 0) {
|
||||
while (chunkTotal > 0) {
|
||||
int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
|
||||
int nRead = in.read(buffer, 0, toRead);
|
||||
out.write(buffer, 0, nRead);
|
||||
chunkTotal -= nRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PerformFullBackupTask extends ObbServiceClient implements Runnable {
|
||||
ParcelFileDescriptor mOutputFile;
|
||||
DeflaterOutputStream mDeflater;
|
||||
IFullBackupRestoreObserver mObserver;
|
||||
boolean mIncludeApks;
|
||||
boolean mIncludeObbs;
|
||||
boolean mIncludeShared;
|
||||
boolean mAllApps;
|
||||
final boolean mIncludeSystem;
|
||||
@@ -2322,6 +2444,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
AtomicBoolean mLatchObject;
|
||||
File mFilesDir;
|
||||
File mManifestFile;
|
||||
|
||||
|
||||
class FullBackupRunner implements Runnable {
|
||||
PackageInfo mPackage;
|
||||
@@ -2377,12 +2500,13 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
}
|
||||
|
||||
PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
|
||||
boolean includeApks, boolean includeShared, String curPassword,
|
||||
String encryptPassword, boolean doAllApps, boolean doSystem, String[] packages,
|
||||
AtomicBoolean latch) {
|
||||
boolean includeApks, boolean includeObbs, boolean includeShared,
|
||||
String curPassword, String encryptPassword, boolean doAllApps,
|
||||
boolean doSystem, String[] packages, AtomicBoolean latch) {
|
||||
mOutputFile = fd;
|
||||
mObserver = observer;
|
||||
mIncludeApks = includeApks;
|
||||
mIncludeObbs = includeObbs;
|
||||
mIncludeShared = includeShared;
|
||||
mAllApps = doAllApps;
|
||||
mIncludeSystem = doSystem;
|
||||
@@ -2405,9 +2529,12 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
|
||||
|
||||
Slog.i(TAG, "--- Performing full-dataset backup ---");
|
||||
|
||||
List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
|
||||
FullBackupObbConnection obbConnection = new FullBackupObbConnection();
|
||||
obbConnection.establish(); // we'll want this later
|
||||
|
||||
sendStartBackup();
|
||||
|
||||
// doAllApps supersedes the package set if any
|
||||
@@ -2557,6 +2684,15 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
for (int i = 0; i < N; i++) {
|
||||
pkg = packagesToBackup.get(i);
|
||||
backupOnePackage(pkg, out);
|
||||
|
||||
// after the app's agent runs to handle its private filesystem
|
||||
// contents, back up any OBB content it has on its behalf.
|
||||
if (mIncludeObbs) {
|
||||
boolean obbOkay = obbConnection.backupObbs(pkg, out);
|
||||
if (!obbOkay) {
|
||||
throw new RuntimeException("Failure writing OBB stack for " + pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Done!
|
||||
@@ -2581,6 +2717,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
mLatchObject.notifyAll();
|
||||
}
|
||||
sendEndBackup();
|
||||
obbConnection.tearDown();
|
||||
if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
|
||||
mWakelock.release();
|
||||
}
|
||||
@@ -2688,20 +2825,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
|
||||
// Now pull data from the app and stuff it into the compressor
|
||||
try {
|
||||
FileInputStream raw = new FileInputStream(pipes[0].getFileDescriptor());
|
||||
DataInputStream in = new DataInputStream(raw);
|
||||
|
||||
byte[] buffer = new byte[16 * 1024];
|
||||
int chunkTotal;
|
||||
while ((chunkTotal = in.readInt()) > 0) {
|
||||
while (chunkTotal > 0) {
|
||||
int toRead = (chunkTotal > buffer.length)
|
||||
? buffer.length : chunkTotal;
|
||||
int nRead = in.read(buffer, 0, toRead);
|
||||
out.write(buffer, 0, nRead);
|
||||
chunkTotal -= nRead;
|
||||
}
|
||||
}
|
||||
routeSocketDataToOutput(pipes[0], out);
|
||||
} catch (IOException e) {
|
||||
Slog.i(TAG, "Caught exception reading from agent", e);
|
||||
}
|
||||
@@ -2900,7 +3024,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
ACCEPT_IF_APK
|
||||
}
|
||||
|
||||
class PerformFullRestoreTask implements Runnable {
|
||||
class PerformFullRestoreTask extends ObbServiceClient implements Runnable {
|
||||
ParcelFileDescriptor mInputFile;
|
||||
String mCurrentPassword;
|
||||
String mDecryptPassword;
|
||||
@@ -2909,6 +3033,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
IBackupAgent mAgent;
|
||||
String mAgentPackage;
|
||||
ApplicationInfo mTargetApp;
|
||||
FullBackupObbConnection mObbConnection = null;
|
||||
ParcelFileDescriptor[] mPipes = null;
|
||||
|
||||
long mBytes;
|
||||
@@ -2937,6 +3062,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
mAgent = null;
|
||||
mAgentPackage = null;
|
||||
mTargetApp = null;
|
||||
mObbConnection = new FullBackupObbConnection();
|
||||
|
||||
// Which packages we've already wiped data on. We prepopulate this
|
||||
// with a whitelist of packages known to be unclearable.
|
||||
@@ -2980,6 +3106,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
@Override
|
||||
public void run() {
|
||||
Slog.i(TAG, "--- Performing full-dataset restore ---");
|
||||
mObbConnection.establish();
|
||||
sendStartRestore();
|
||||
|
||||
// Are we able to restore shared-storage data?
|
||||
@@ -3067,6 +3194,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
mLatchObject.set(true);
|
||||
mLatchObject.notifyAll();
|
||||
}
|
||||
mObbConnection.tearDown();
|
||||
sendEndRestore();
|
||||
Slog.d(TAG, "Full restore pass complete.");
|
||||
mWakelock.release();
|
||||
@@ -3319,22 +3447,30 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
long toCopy = info.size;
|
||||
final int token = generateToken();
|
||||
try {
|
||||
if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
|
||||
+ info.path);
|
||||
prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
|
||||
// fire up the app's agent listening on the socket. If
|
||||
// the agent is running in the system process we can't
|
||||
// just invoke it asynchronously, so we provide a thread
|
||||
// for it here.
|
||||
if (mTargetApp.processName.equals("system")) {
|
||||
Slog.d(TAG, "system process agent - spinning a thread");
|
||||
RestoreFileRunnable runner = new RestoreFileRunnable(
|
||||
mAgent, info, mPipes[0], token);
|
||||
new Thread(runner).start();
|
||||
if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
|
||||
if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
|
||||
+ " : " + info.path);
|
||||
mObbConnection.restoreObbFile(pkg, mPipes[0],
|
||||
info.size, info.type, info.path, info.mode,
|
||||
info.mtime, token, mBackupManagerBinder);
|
||||
} else {
|
||||
mAgent.doRestoreFile(mPipes[0], info.size, info.type,
|
||||
info.domain, info.path, info.mode, info.mtime,
|
||||
token, mBackupManagerBinder);
|
||||
if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
|
||||
+ info.path);
|
||||
// fire up the app's agent listening on the socket. If
|
||||
// the agent is running in the system process we can't
|
||||
// just invoke it asynchronously, so we provide a thread
|
||||
// for it here.
|
||||
if (mTargetApp.processName.equals("system")) {
|
||||
Slog.d(TAG, "system process agent - spinning a thread");
|
||||
RestoreFileRunnable runner = new RestoreFileRunnable(
|
||||
mAgent, info, mPipes[0], token);
|
||||
new Thread(runner).start();
|
||||
} else {
|
||||
mAgent.doRestoreFile(mPipes[0], info.size, info.type,
|
||||
info.domain, info.path, info.mode, info.mtime,
|
||||
token, mBackupManagerBinder);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// couldn't dup the socket for a process-local restore
|
||||
@@ -3342,7 +3478,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
agentSuccess = false;
|
||||
okay = false;
|
||||
} catch (RemoteException e) {
|
||||
// whoops, remote agent went away. We'll eat the content
|
||||
// whoops, remote entity went away. We'll eat the content
|
||||
// ourselves, then, and not copy it over.
|
||||
Slog.e(TAG, "Agent crashed during full restore");
|
||||
agentSuccess = false;
|
||||
@@ -3891,18 +4027,6 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
slash = info.path.indexOf('/');
|
||||
if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
|
||||
info.domain = info.path.substring(0, slash);
|
||||
// validate that it's one of the domains we understand
|
||||
if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
|
||||
&& !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
|
||||
throw new IOException("Unrecognized domain " + info.domain);
|
||||
}
|
||||
|
||||
info.path = info.path.substring(slash + 1);
|
||||
}
|
||||
}
|
||||
@@ -4989,7 +5113,8 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
// Run a *full* backup pass for the given package, writing the resulting data stream
|
||||
// to the supplied file descriptor. This method is synchronous and does not return
|
||||
// to the caller until the backup has been completed.
|
||||
public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
|
||||
public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
|
||||
boolean includeObbs, boolean includeShared,
|
||||
boolean doAllApps, boolean includeSystem, String[] pkgList) {
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
|
||||
|
||||
@@ -5020,12 +5145,12 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
}
|
||||
|
||||
if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
|
||||
+ " shared=" + includeShared + " all=" + doAllApps
|
||||
+ " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
|
||||
+ " pkgs=" + pkgList);
|
||||
Slog.i(TAG, "Beginning full backup...");
|
||||
|
||||
FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
|
||||
doAllApps, includeSystem, pkgList);
|
||||
FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
|
||||
includeShared, doAllApps, includeSystem, pkgList);
|
||||
final int token = generateToken();
|
||||
synchronized (mFullConfirmations) {
|
||||
mFullConfirmations.put(token, params);
|
||||
|
||||
Reference in New Issue
Block a user