Merge "Implement shared-storage full backup/restore"
This commit is contained in:
committed by
Android (Google) Code Review
commit
ffccff0864
@ -278,7 +278,6 @@ public abstract class BackupAgent extends ContextWrapper {
|
||||
int token, IBackupManager callbackBinder) throws RemoteException {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
Log.d(TAG, "doRestoreFile() => onRestoreFile()");
|
||||
BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -46,7 +46,7 @@ public class FullBackup {
|
||||
public static final String SHARED_STORAGE_TOKEN = "shared";
|
||||
|
||||
public static final String APPS_PREFIX = "apps/";
|
||||
public static final String SHARED_PREFIX = "shared/";
|
||||
public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
|
||||
|
||||
public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
|
||||
public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
|
||||
@ -61,7 +61,8 @@ public class FullBackup {
|
||||
String linkdomain, String rootpath, String path, BackupDataOutput output);
|
||||
|
||||
static public void restoreToFile(ParcelFileDescriptor data,
|
||||
long size, int type, long mode, long mtime, File outFile) throws IOException {
|
||||
long size, int type, long mode, long mtime, File outFile,
|
||||
boolean doChmod) throws IOException {
|
||||
if (type == FullBackup.TYPE_DIRECTORY) {
|
||||
// Canonically a directory has no associated content, so we don't need to read
|
||||
// anything from the pipe in this case. Just create the directory here and
|
||||
@ -116,7 +117,7 @@ public class FullBackup {
|
||||
}
|
||||
|
||||
// Now twiddle the state to match the backup, assuming all went well
|
||||
if (outFile != null) {
|
||||
if (doChmod && outFile != null) {
|
||||
try {
|
||||
Libcore.os.chmod(outFile.getPath(), (int)mode);
|
||||
} catch (ErrnoException e) {
|
||||
|
@ -28,8 +28,6 @@ import libcore.io.OsConstants;
|
||||
import libcore.io.StructStat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
@ -84,9 +82,10 @@ public class FullBackupAgent extends BackupAgent {
|
||||
|
||||
@Override
|
||||
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
||||
ParcelFileDescriptor newState) {
|
||||
ParcelFileDescriptor newState) throws IOException {
|
||||
// Filters, the scan queue, and the set of resulting entities
|
||||
HashSet<String> filterSet = new HashSet<String>();
|
||||
String packageName = getPackageName();
|
||||
|
||||
// Okay, start with the app's root tree, but exclude all of the canonical subdirs
|
||||
if (mLibDir != null) {
|
||||
@ -96,25 +95,28 @@ public class FullBackupAgent extends BackupAgent {
|
||||
filterSet.add(mDatabaseDir);
|
||||
filterSet.add(mSharedPrefsDir);
|
||||
filterSet.add(mFilesDir);
|
||||
processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
|
||||
processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
|
||||
|
||||
// Now do the same for the files dir, db dir, and shared prefs dir
|
||||
filterSet.add(mMainDir);
|
||||
filterSet.remove(mFilesDir);
|
||||
processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
|
||||
processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
|
||||
|
||||
filterSet.add(mFilesDir);
|
||||
filterSet.remove(mDatabaseDir);
|
||||
processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
|
||||
processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
|
||||
|
||||
filterSet.add(mDatabaseDir);
|
||||
filterSet.remove(mSharedPrefsDir);
|
||||
processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
|
||||
processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
|
||||
}
|
||||
|
||||
private void processTree(String domain, String rootPath,
|
||||
// Scan the dir tree (if it actually exists) and process each entry we find. If the
|
||||
// 'excludes' parameter is non-null, it is consulted each time a new file system entity
|
||||
// is visited to see whether that entity (and its subtree, if appropriate) should be
|
||||
// omitted from the backup process.
|
||||
protected void processTree(String packageName, String domain, String rootPath,
|
||||
HashSet<String> excludes, BackupDataOutput data) {
|
||||
// Scan the dir tree (if it actually exists) and process each entry we find
|
||||
File rootFile = new File(rootPath);
|
||||
if (rootFile.exists()) {
|
||||
LinkedList<File> scanQueue = new LinkedList<File>();
|
||||
@ -125,7 +127,7 @@ public class FullBackupAgent extends BackupAgent {
|
||||
String filePath = file.getAbsolutePath();
|
||||
|
||||
// prune this subtree?
|
||||
if (excludes.contains(filePath)) {
|
||||
if (excludes != null && excludes.contains(filePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -149,7 +151,7 @@ public class FullBackupAgent extends BackupAgent {
|
||||
}
|
||||
|
||||
// Finally, back this file up before proceeding
|
||||
FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
|
||||
FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,6 +220,6 @@ public class FullBackupAgent extends BackupAgent {
|
||||
if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
|
||||
|
||||
// Now that we've figured out where the data goes, send it on its way
|
||||
FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
|
||||
FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
|
||||
}
|
||||
}
|
||||
|
@ -1517,11 +1517,12 @@ public class PackageParser {
|
||||
}
|
||||
}
|
||||
|
||||
// fullBackupAgent is explicitly handled even if allowBackup is false
|
||||
name = sa.getNonConfigurationString(
|
||||
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
|
||||
if (name != null) {
|
||||
ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
|
||||
if (true) {
|
||||
if (false) {
|
||||
Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
|
||||
+ " from " + pkgName + "+" + name);
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ static struct {
|
||||
static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
|
||||
jstring domainObj, jstring linkdomain,
|
||||
jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
|
||||
int ret;
|
||||
|
||||
// Extract the various strings, allowing for null object pointers
|
||||
const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
|
||||
const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
|
||||
|
@ -503,10 +503,10 @@ int write_tarfile(const String8& packageName, const String8& domain,
|
||||
needExtended = true;
|
||||
}
|
||||
|
||||
// Non-7bit-clean path also means needing pax extended format
|
||||
// Non-7bit-clean path or embedded spaces also mean needing pax extended format
|
||||
if (!needExtended) {
|
||||
for (size_t i = 0; i < filepath.length(); i++) {
|
||||
if ((filepath[i] & 0x80) != 0) {
|
||||
if ((filepath[i] & 0x80) != 0 || filepath[i] == ' ') {
|
||||
needExtended = true;
|
||||
break;
|
||||
}
|
||||
|
33
packages/SharedStorageBackup/Android.mk
Normal file
33
packages/SharedStorageBackup/Android.mk
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright (C) 2011 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
|
||||
|
||||
LOCAL_PACKAGE_NAME := SharedStorageBackup
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
########################
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
|
29
packages/SharedStorageBackup/AndroidManifest.xml
Normal file
29
packages/SharedStorageBackup/AndroidManifest.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
** Copyright 2011, 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.sharedstoragebackup" >
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
|
||||
|
||||
<application android:allowClearUserData="false"
|
||||
android:permission="android.permission.CONFIRM_FULL_BACKUP"
|
||||
android:fullBackupAgent=".SharedStorageAgent"
|
||||
android:allowBackup="false" >
|
||||
</application>
|
||||
</manifest>
|
1
packages/SharedStorageBackup/proguard.flags
Normal file
1
packages/SharedStorageBackup/proguard.flags
Normal file
@ -0,0 +1 @@
|
||||
-keep class com.android.sharedstoragebackup.SharedStorageAgent
|
@ -0,0 +1,93 @@
|
||||
package com.android.sharedstoragebackup;
|
||||
|
||||
import android.app.backup.FullBackup;
|
||||
import android.app.backup.FullBackupAgent;
|
||||
import android.app.backup.BackupDataInput;
|
||||
import android.app.backup.BackupDataOutput;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.StorageVolume;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SharedStorageAgent extends FullBackupAgent {
|
||||
static final String TAG = "SharedStorageAgent";
|
||||
static final boolean DEBUG = true;
|
||||
|
||||
StorageVolume[] mVolumes;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
|
||||
if (mgr != null) {
|
||||
mVolumes = mgr.getVolumeList();
|
||||
} else {
|
||||
Slog.e(TAG, "Unable to access Storage Manager");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
||||
ParcelFileDescriptor newState) throws IOException {
|
||||
// If there are shared-storage volumes available, run the inherited directory-
|
||||
// hierarchy backup process on them. By convention in the Storage Manager, the
|
||||
// "primary" shared storage volume is first in the list.
|
||||
if (mVolumes != null) {
|
||||
for (int i = 0; i < mVolumes.length; i++) {
|
||||
StorageVolume v = mVolumes[i];
|
||||
// Express the contents of volume N this way in the tar stream:
|
||||
// shared/N/path/to/file
|
||||
// The restore will then extract to the given volume
|
||||
String domain = FullBackup.SHARED_PREFIX + i;
|
||||
processTree(null, domain, v.getPath(), null, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incremental onRestore() implementation is not used.
|
||||
*/
|
||||
@Override
|
||||
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
|
||||
throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Full restore of one file to shared storage
|
||||
*/
|
||||
@Override
|
||||
public void onRestoreFile(ParcelFileDescriptor data, long size,
|
||||
int type, String domain, String relpath, long mode, long mtime)
|
||||
throws IOException {
|
||||
Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
|
||||
|
||||
File outFile = null;
|
||||
|
||||
// The file path must be in the semantic form [number]/path/to/file...
|
||||
int slash = relpath.indexOf('/');
|
||||
if (slash > 0) {
|
||||
try {
|
||||
int i = Integer.parseInt(relpath.substring(0, slash));
|
||||
if (i <= mVolumes.length) {
|
||||
outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
|
||||
if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
|
||||
} else {
|
||||
Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
|
||||
}
|
||||
if (outFile == null) {
|
||||
Slog.e(TAG, "Skipping data with malformed path " + relpath);
|
||||
}
|
||||
|
||||
FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false);
|
||||
}
|
||||
}
|
@ -134,6 +134,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
// Timeout intervals for agent backup & restore operations
|
||||
static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
|
||||
static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
|
||||
static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
|
||||
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
|
||||
|
||||
// User confirmation timeout for a full backup/restore operation
|
||||
@ -1691,7 +1692,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
public void run() {
|
||||
final List<PackageInfo> packagesToBackup;
|
||||
|
||||
Slog.i(TAG, "--- Performing full-dataset restore ---");
|
||||
Slog.i(TAG, "--- Performing full-dataset backup ---");
|
||||
sendStartBackup();
|
||||
|
||||
// doAllApps supersedes the package set if any
|
||||
@ -1720,64 +1721,23 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
// Now back up the app data via the agent mechanism
|
||||
PackageInfo pkg = null;
|
||||
try {
|
||||
// Now back up the app data via the agent mechanism
|
||||
int N = packagesToBackup.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
pkg = packagesToBackup.get(i);
|
||||
backupOnePackage(pkg);
|
||||
}
|
||||
|
||||
Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
|
||||
|
||||
IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
|
||||
IApplicationThread.BACKUP_MODE_FULL);
|
||||
if (agent != null) {
|
||||
try {
|
||||
ApplicationInfo app = pkg.applicationInfo;
|
||||
boolean sendApk = mIncludeApks
|
||||
&& ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
|
||||
&& ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
|
||||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
|
||||
|
||||
sendOnBackupPackage(pkg.packageName);
|
||||
|
||||
{
|
||||
BackupDataOutput output = new BackupDataOutput(
|
||||
mOutputFile.getFileDescriptor());
|
||||
|
||||
if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
|
||||
writeAppManifest(pkg, mManifestFile, sendApk);
|
||||
FullBackup.backupToTar(pkg.packageName, null, null,
|
||||
mFilesDir.getAbsolutePath(),
|
||||
mManifestFile.getAbsolutePath(),
|
||||
output);
|
||||
}
|
||||
|
||||
if (DEBUG) Slog.d(TAG, "Calling doBackup()");
|
||||
final int token = generateToken();
|
||||
prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
|
||||
agent.doBackup(null, mOutputFile, null, sendApk,
|
||||
token, mBackupManagerBinder);
|
||||
boolean success = waitUntilOperationComplete(token);
|
||||
if (!success) {
|
||||
Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
|
||||
} else {
|
||||
if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Error backing up " + pkg.packageName, e);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
|
||||
}
|
||||
tearDown(pkg);
|
||||
// Finally, shared storage if requested
|
||||
if (mIncludeShared) {
|
||||
backupSharedStorage();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "App died during full backup");
|
||||
} finally {
|
||||
if (pkg != null) {
|
||||
tearDown(pkg);
|
||||
}
|
||||
tearDown(pkg);
|
||||
try {
|
||||
mOutputFile.close();
|
||||
} catch (IOException e) {
|
||||
@ -1796,6 +1756,79 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void backupOnePackage(PackageInfo pkg) throws RemoteException {
|
||||
Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
|
||||
|
||||
IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
|
||||
IApplicationThread.BACKUP_MODE_FULL);
|
||||
if (agent != null) {
|
||||
try {
|
||||
ApplicationInfo app = pkg.applicationInfo;
|
||||
boolean sendApk = mIncludeApks
|
||||
&& ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
|
||||
&& ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
|
||||
(app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
|
||||
|
||||
sendOnBackupPackage(pkg.packageName);
|
||||
|
||||
{
|
||||
BackupDataOutput output = new BackupDataOutput(
|
||||
mOutputFile.getFileDescriptor());
|
||||
|
||||
if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
|
||||
writeAppManifest(pkg, mManifestFile, sendApk);
|
||||
FullBackup.backupToTar(pkg.packageName, null, null,
|
||||
mFilesDir.getAbsolutePath(),
|
||||
mManifestFile.getAbsolutePath(),
|
||||
output);
|
||||
}
|
||||
|
||||
if (DEBUG) Slog.d(TAG, "Calling doBackup()");
|
||||
final int token = generateToken();
|
||||
prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
|
||||
agent.doBackup(null, mOutputFile, null, sendApk,
|
||||
token, mBackupManagerBinder);
|
||||
if (!waitUntilOperationComplete(token)) {
|
||||
Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
|
||||
} else {
|
||||
if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Error backing up " + pkg.packageName, e);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
|
||||
}
|
||||
tearDown(pkg);
|
||||
}
|
||||
|
||||
private void backupSharedStorage() throws RemoteException {
|
||||
PackageInfo pkg = null;
|
||||
try {
|
||||
pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
|
||||
IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
|
||||
IApplicationThread.BACKUP_MODE_FULL);
|
||||
if (agent != null) {
|
||||
sendOnBackupPackage("Shared storage");
|
||||
|
||||
final int token = generateToken();
|
||||
prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
|
||||
agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
|
||||
if (!waitUntilOperationComplete(token)) {
|
||||
Slog.e(TAG, "Full backup failed on shared storage");
|
||||
} else {
|
||||
if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Could not bind to shared storage backup agent");
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
Slog.e(TAG, "Shared storage backup package not found");
|
||||
} finally {
|
||||
tearDown(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
|
||||
throws IOException {
|
||||
// Manifest format. All data are strings ending in LF:
|
||||
@ -1836,23 +1869,24 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
}
|
||||
|
||||
private void tearDown(PackageInfo pkg) {
|
||||
final ApplicationInfo app = pkg.applicationInfo;
|
||||
try {
|
||||
// unbind and tidy up even on timeout or failure, just in case
|
||||
mActivityManager.unbindBackupAgent(app);
|
||||
if (pkg != null) {
|
||||
final ApplicationInfo app = pkg.applicationInfo;
|
||||
if (app != null) {
|
||||
try {
|
||||
// unbind and tidy up even on timeout or failure, just in case
|
||||
mActivityManager.unbindBackupAgent(app);
|
||||
|
||||
// The agent was running with a stub Application object, so shut it down.
|
||||
// !!! We hardcode the confirmation UI's package name here rather than use a
|
||||
// manifest flag! TODO something less direct.
|
||||
if (app.uid != Process.SYSTEM_UID
|
||||
&& !pkg.packageName.equals("com.android.backupconfirm")) {
|
||||
if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
|
||||
mActivityManager.killApplicationProcess(app.processName, app.uid);
|
||||
} else {
|
||||
if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
|
||||
// The agent was running with a stub Application object, so shut it down.
|
||||
if (app.uid != Process.SYSTEM_UID) {
|
||||
if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
|
||||
mActivityManager.killApplicationProcess(app.processName, app.uid);
|
||||
} else {
|
||||
if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Lost app trying to shut down");
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Lost app trying to shut down");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1949,6 +1983,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
// with a whitelist of packages known to be unclearable.
|
||||
mClearedPackages.add("android");
|
||||
mClearedPackages.add("com.android.providers.settings");
|
||||
|
||||
}
|
||||
|
||||
class RestoreFileRunnable implements Runnable {
|
||||
@ -1988,6 +2023,11 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
Slog.i(TAG, "--- Performing full-dataset restore ---");
|
||||
sendStartRestore();
|
||||
|
||||
// Are we able to restore shared-storage data?
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
|
||||
@ -2707,7 +2747,9 @@ class BackupManagerService extends IBackupManager.Stub {
|
||||
info.path, 0, FullBackup.SHARED_PREFIX.length())) {
|
||||
// File in shared storage. !!! TODO: implement this.
|
||||
info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
|
||||
info.packageName = "com.android.sharedstoragebackup";
|
||||
info.domain = FullBackup.SHARED_STORAGE_TOKEN;
|
||||
if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
|
||||
} else if (FullBackup.APPS_PREFIX.regionMatches(0,
|
||||
info.path, 0, FullBackup.APPS_PREFIX.length())) {
|
||||
// App content! Parse out the package name and domain
|
||||
|
@ -138,7 +138,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
|
||||
if (outFile == null) {
|
||||
Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
|
||||
}
|
||||
FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
|
||||
FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
|
||||
|
||||
if (restoredWallpaper) {
|
||||
WallpaperManagerService wallpaper =
|
||||
|
Reference in New Issue
Block a user