From b3177dc9e5aaa28a88a690147c5bf8cee4baeb4d Mon Sep 17 00:00:00 2001 From: Artem Iglikov Date: Wed, 26 Apr 2017 09:05:32 +0100 Subject: [PATCH] Move more common methods to TarBackupReader. This moves rest of common methods in FullRestoreEngine and PerformAdbRestoreTask to TarBackupReader and FileMetadata. Bug: 37519282 Test: gts Change-Id: Iff1f95e87721e74c9c5764ab98c64026604eee9b --- .../android/server/backup/FileMetadata.java | 35 ++ .../RefactoredBackupManagerService.java | 32 - .../PerformFullTransportBackupTask.java | 4 +- .../backup/internal/PerformBackupTask.java | 4 +- .../backup/restore/FullRestoreEngine.java | 413 +------------ .../backup/restore/PerformAdbRestoreTask.java | 280 +-------- .../restore/PerformUnifiedRestoreTask.java | 8 +- .../utils/BackupManagerMonitorUtils.java | 32 + .../server/backup/utils/TarBackupReader.java | 579 +++++++++++++++--- 9 files changed, 592 insertions(+), 795 deletions(-) diff --git a/services/backup/java/com/android/server/backup/FileMetadata.java b/services/backup/java/com/android/server/backup/FileMetadata.java index 4ece6391c21dc..afddd3e725e1e 100644 --- a/services/backup/java/com/android/server/backup/FileMetadata.java +++ b/services/backup/java/com/android/server/backup/FileMetadata.java @@ -16,6 +16,12 @@ package com.android.server.backup; +import android.app.backup.BackupAgent; +import android.util.Slog; + +import java.text.SimpleDateFormat; +import java.util.Date; + /** * Description of a file in the restore datastream. */ @@ -46,4 +52,33 @@ public class FileMetadata { sb.append('}'); return sb.toString(); } + + public void dump() { + StringBuilder b = new StringBuilder(128); + + // mode string + b.append((type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); + b.append(((mode & 0400) != 0) ? 'r' : '-'); + b.append(((mode & 0200) != 0) ? 'w' : '-'); + b.append(((mode & 0100) != 0) ? 'x' : '-'); + b.append(((mode & 0040) != 0) ? 'r' : '-'); + b.append(((mode & 0020) != 0) ? 'w' : '-'); + b.append(((mode & 0010) != 0) ? 'x' : '-'); + b.append(((mode & 0004) != 0) ? 'r' : '-'); + b.append(((mode & 0002) != 0) ? 'w' : '-'); + b.append(((mode & 0001) != 0) ? 'x' : '-'); + b.append(String.format(" %9d ", size)); + + Date stamp = new Date(mtime); + b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); + + b.append(packageName); + b.append(" :: "); + b.append(domain); + b.append(" :: "); + b.append(path); + + Slog.i(RefactoredBackupManagerService.TAG, b.toString()); + } + } diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 00960cfe4e64f..151354bb79369 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -3654,38 +3654,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } - public Bundle putMonitoringExtra(Bundle extras, String key, String value) { - if (extras == null) { - extras = new Bundle(); - } - extras.putString(key, value); - return extras; - } - - private Bundle putMonitoringExtra(Bundle extras, String key, int value) { - if (extras == null) { - extras = new Bundle(); - } - extras.putInt(key, value); - return extras; - } - - public Bundle putMonitoringExtra(Bundle extras, String key, long value) { - if (extras == null) { - extras = new Bundle(); - } - extras.putLong(key, value); - return extras; - } - - - public Bundle putMonitoringExtra(Bundle extras, String key, boolean value) { - if (extras == null) { - extras = new Bundle(); - } - extras.putBoolean(key, value); - return extras; - } @Override public IBackupManager getBackupManagerBinder() { diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 5e7069efdb65d..334d2145cf8ff 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -364,7 +364,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT, mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService.putMonitoringExtra(null, + BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_PREFLIGHT_ERROR, preflightResult)); backupPackageStatus = (int) preflightResult; @@ -545,7 +545,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP, mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService.putMonitoringExtra(null, + BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_EXCEPTION_FULL_BACKUP, Log.getStackTraceString(e))); diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java index 81da8290a218d..d757dbeaec591 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java @@ -842,7 +842,7 @@ public class PerformBackupTask implements BackupRestoreTask { mCurrentPackage, BackupManagerMonitor .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService.putMonitoringExtra(null, + BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY, key)); backupManagerService.getBackupHandler().removeMessages( @@ -1027,7 +1027,7 @@ public class PerformBackupTask implements BackupRestoreTask { mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL, mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, - backupManagerService.putMonitoringExtra(null, + BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, mCancelAll)); errorCleanup(); diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index f532b3a651547..61c703c167d0d 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -16,29 +16,9 @@ package com.android.server.backup.restore; -import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME; -import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION; -import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_MANIFEST_PACKAGE_NAME; -import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OLD_VERSION; -import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_POLICY_ALLOW_APKS; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH; -import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER; - import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; import android.app.PackageInstallObserver; -import android.app.backup.BackupAgent; -import android.app.backup.BackupManagerMonitor; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IFullBackupRestoreObserver; @@ -60,17 +40,12 @@ import com.android.server.backup.FileMetadata; import com.android.server.backup.RefactoredBackupManagerService; import com.android.server.backup.fullbackup.FullBackupObbConnection; import com.android.server.backup.utils.AppBackupUtils; -import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.TarBackupReader; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; @@ -206,7 +181,7 @@ public class FullRestoreEngine extends RestoreEngine { public void onBytesRead(long bytesRead) { mBytes += bytesRead; } - }); + }, mMonitor); FileMetadata info; try { @@ -216,7 +191,7 @@ public class FullRestoreEngine extends RestoreEngine { info = tarBackupReader.readTarHeaders(); if (info != null) { if (RefactoredBackupManagerService.MORE_DEBUG) { - dumpFileMetadata(info); + info.dump(); } final String pkg = info.packageName; @@ -256,7 +231,10 @@ public class FullRestoreEngine extends RestoreEngine { } if (info.path.equals(RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME)) { - mPackagePolicies.put(pkg, readAppManifest(info, instream)); + RestorePolicy appManifest = tarBackupReader.readAppManifest( + backupManagerService.getPackageManager(), mAllowApks, + mManifestSignatures, info); + mPackagePolicies.put(pkg, appManifest); mPackageInstallers.put(pkg, info.installerPackageName); // We've read only the manifest content itself at this point, // so consume the footer before looping around to the next @@ -266,7 +244,16 @@ public class FullRestoreEngine extends RestoreEngine { } else if (info.path.equals( RefactoredBackupManagerService.BACKUP_METADATA_FILENAME)) { // Metadata blobs! - readMetadata(info, instream); + tarBackupReader.readMetadata(info); + + // The following only exist because we want to keep refactoring as safe as + // possible, without changing too much. + // TODO: Refactor, so that there are no funny things like this. + // This is read during TarBackupReader.readMetadata(). + mWidgetData = tarBackupReader.getWidgetData(); + // This can be nulled during TarBackupReader.readMetadata(). + mMonitor = tarBackupReader.getMonitor(); + tarBackupReader.skipTarPadding(info.size); } else { // Non-manifest, so it's actual file data. Is this a package @@ -812,374 +799,6 @@ public class FullRestoreEngine extends RestoreEngine { return okay; } - // Read a widget metadata file, returning the restored blob - void readMetadata(FileMetadata info, InputStream instream) throws IOException { - // Fail on suspiciously large widget dump files - if (info.size > 64 * 1024) { - throw new IOException("Metadata too big; corrupt? size=" + info.size); - } - - byte[] buffer = new byte[(int) info.size]; - if (TarBackupReader.readExactly(instream, buffer, 0, (int) info.size) == info.size) { - mBytes += info.size; - } else { - throw new IOException("Unexpected EOF in widget data"); - } - - String[] str = new String[1]; - int offset = TarBackupReader.extractLine(buffer, 0, str); - int version = Integer.parseInt(str[0]); - if (version == RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION) { - offset = TarBackupReader.extractLine(buffer, offset, str); - final String pkg = str[0]; - if (info.packageName.equals(pkg)) { - // Data checks out -- the rest of the buffer is a concatenation of - // binary blobs as described in the comment at writeAppWidgetData() - ByteArrayInputStream bin = new ByteArrayInputStream(buffer, - offset, buffer.length - offset); - DataInputStream in = new DataInputStream(bin); - while (bin.available() > 0) { - int token = in.readInt(); - int size = in.readInt(); - if (size > 64 * 1024) { - throw new IOException("Datum " - + Integer.toHexString(token) - + " too big; corrupt? size=" + info.size); - } - switch (token) { - case RefactoredBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN: { - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Got widget metadata for " + info.packageName); - } - mWidgetData = new byte[size]; - in.read(mWidgetData); - break; - } - default: { - if (RefactoredBackupManagerService.DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, "Ignoring metadata blob " - + Integer.toHexString(token) - + " for " + info.packageName); - } - in.skipBytes(size); - break; - } - } - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, - "Metadata mismatch: package " + info.packageName - + " but widget data for " + pkg); - - Bundle monitoringExtras = backupManagerService.putMonitoringExtra(null, - EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); - monitoringExtras = backupManagerService.putMonitoringExtra(monitoringExtras, - BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - monitoringExtras); - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, "Unsupported metadata version " + version); - - Bundle monitoringExtras = backupManagerService - .putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, - info.packageName); - monitoringExtras = backupManagerService.putMonitoringExtra(monitoringExtras, - EXTRA_LOG_EVENT_PACKAGE_VERSION, version); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - monitoringExtras); - } - } - - // Returns a policy constant - RestorePolicy readAppManifest(FileMetadata info, InputStream instream) - throws IOException { - // Fail on suspiciously large manifest files - if (info.size > 64 * 1024) { - throw new IOException("Restore manifest too big; corrupt? size=" + info.size); - } - - byte[] buffer = new byte[(int) info.size]; - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - " readAppManifest() looking for " + info.size + " bytes, " - + mBytes + " already consumed"); - } - if (TarBackupReader.readExactly(instream, buffer, 0, (int) info.size) == info.size) { - mBytes += info.size; - } else { - throw new IOException("Unexpected EOF in manifest"); - } - - RestorePolicy policy = RestorePolicy.IGNORE; - String[] str = new String[1]; - int offset = 0; - - try { - offset = TarBackupReader.extractLine(buffer, offset, str); - int version = Integer.parseInt(str[0]); - if (version == RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION) { - offset = TarBackupReader.extractLine(buffer, offset, str); - String manifestPackage = str[0]; - // TODO: handle - if (manifestPackage.equals(info.packageName)) { - offset = TarBackupReader.extractLine(buffer, offset, str); - version = Integer.parseInt(str[0]); // app version - offset = TarBackupReader.extractLine(buffer, offset, str); - // This is the platform version, which we don't use, but we parse it - // as a safety against corruption in the manifest. - Integer.parseInt(str[0]); - offset = TarBackupReader.extractLine(buffer, offset, str); - info.installerPackageName = (str[0].length() > 0) ? str[0] : null; - offset = TarBackupReader.extractLine(buffer, offset, str); - boolean hasApk = str[0].equals("1"); - offset = TarBackupReader.extractLine(buffer, offset, str); - int numSigs = Integer.parseInt(str[0]); - if (numSigs > 0) { - Signature[] sigs = new Signature[numSigs]; - for (int i = 0; i < numSigs; i++) { - offset = TarBackupReader.extractLine(buffer, offset, str); - sigs[i] = new Signature(str[0]); - } - mManifestSignatures.put(info.packageName, sigs); - - // Okay, got the manifest info we need... - try { - PackageInfo pkgInfo = - backupManagerService.getPackageManager().getPackageInfo( - info.packageName, PackageManager.GET_SIGNATURES); - // Fall through to IGNORE if the app explicitly disallows backup - final int flags = pkgInfo.applicationInfo.flags; - if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { - // Restore system-uid-space packages only if they have - // defined a custom backup agent - if ((pkgInfo.applicationInfo.uid - >= Process.FIRST_APPLICATION_UID) - || (pkgInfo.applicationInfo.backupAgentName != null)) { - // Verify signatures against any installed version; if they - // don't match, then we fall though and ignore the data. The - // signatureMatch() method explicitly ignores the signature - // check for packages installed on the system partition, because - // such packages are signed with the platform cert instead of - // the app developer's cert, so they're different on every - // device. - if (AppBackupUtils.signaturesMatch(sigs, - pkgInfo)) { - if ((pkgInfo.applicationInfo.flags - & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) { - Slog.i(RefactoredBackupManagerService.TAG, - "Package has restoreAnyVersion; taking data"); - mMonitor = BackupManagerMonitorUtils.monitorEvent( - mMonitor, - LOG_EVENT_ID_RESTORE_ANY_VERSION, - pkgInfo, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - null); - policy = RestorePolicy.ACCEPT; - } else if (pkgInfo.versionCode >= version) { - Slog.i(RefactoredBackupManagerService.TAG, - "Sig + version match; taking data"); - policy = RestorePolicy.ACCEPT; - mMonitor = BackupManagerMonitorUtils.monitorEvent( - mMonitor, - LOG_EVENT_ID_VERSIONS_MATCH, - pkgInfo, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - null); - } else { - // The data is from a newer version of the app than - // is presently installed. That means we can only - // use it if the matching apk is also supplied. - if (mAllowApks) { - Slog.i(RefactoredBackupManagerService.TAG, - "Data version " + version - + " is newer than installed " - + "version " - + pkgInfo.versionCode - + " - requiring apk"); - policy = RestorePolicy.ACCEPT_IF_APK; - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Data requires newer version " - + version + "; ignoring"); - mMonitor = BackupManagerMonitorUtils - .monitorEvent(mMonitor, - LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER, - pkgInfo, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService - .putMonitoringExtra( - null, - EXTRA_LOG_OLD_VERSION, - version)); - - policy = RestorePolicy.IGNORE; - } - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, - "Restore manifest signatures do not match " - + "installed application for " - + info.packageName); - mMonitor = BackupManagerMonitorUtils.monitorEvent( - mMonitor, - LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH, - pkgInfo, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - null); - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, - "Package " + info.packageName - + " is system level with no agent"); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - LOG_EVENT_ID_SYSTEM_APP_NO_AGENT, - pkgInfo, - LOG_EVENT_CATEGORY_AGENT, - null); - } - } else { - if (RefactoredBackupManagerService.DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Restore manifest from " - + info.packageName + " but allowBackup=false"); - } - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE, - pkgInfo, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - null); - } - } catch (NameNotFoundException e) { - // Okay, the target app isn't installed. We can process - // the restore properly only if the dataset provides the - // apk file and we can successfully install it. - if (mAllowApks) { - if (RefactoredBackupManagerService.DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Package " + info.packageName - + " not installed; requiring apk in dataset"); - } - policy = RestorePolicy.ACCEPT_IF_APK; - } else { - policy = RestorePolicy.IGNORE; - } - Bundle monitoringExtras = backupManagerService.putMonitoringExtra(null, - EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); - monitoringExtras = backupManagerService.putMonitoringExtra( - monitoringExtras, - EXTRA_LOG_POLICY_ALLOW_APKS, mAllowApks); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - LOG_EVENT_ID_APK_NOT_INSTALLED, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - monitoringExtras); - } - - if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { - Slog.i(RefactoredBackupManagerService.TAG, - "Cannot restore package " + info.packageName - + " without the matching .apk"); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService.putMonitoringExtra(null, - EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); - } - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Missing signature on backed-up package " - + info.packageName); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - LOG_EVENT_ID_MISSING_SIGNATURE, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService.putMonitoringExtra(null, - EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); - } - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Expected package " + info.packageName - + " but restore manifest claims " + manifestPackage); - Bundle monitoringExtras = backupManagerService.putMonitoringExtra(null, - EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); - monitoringExtras = backupManagerService.putMonitoringExtra(monitoringExtras, - EXTRA_LOG_MANIFEST_PACKAGE_NAME, manifestPackage); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - monitoringExtras); - } - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Unknown restore manifest version " + version - + " for package " + info.packageName); - Bundle monitoringExtras = backupManagerService.putMonitoringExtra(null, - EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); - monitoringExtras = backupManagerService.putMonitoringExtra(monitoringExtras, - EXTRA_LOG_EVENT_PACKAGE_VERSION, version); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - monitoringExtras); - - } - } catch (NumberFormatException e) { - Slog.w(RefactoredBackupManagerService.TAG, - "Corrupt restore manifest for package " + info.packageName); - mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, - BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST, - null, - LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, - backupManagerService.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, - info.packageName)); - } catch (IllegalArgumentException e) { - Slog.w(RefactoredBackupManagerService.TAG, e.getMessage()); - } - - return policy; - } - - void dumpFileMetadata(FileMetadata info) { - if (RefactoredBackupManagerService.MORE_DEBUG) { - StringBuilder b = new StringBuilder(128); - - // mode string - b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); - b.append(((info.mode & 0400) != 0) ? 'r' : '-'); - b.append(((info.mode & 0200) != 0) ? 'w' : '-'); - b.append(((info.mode & 0100) != 0) ? 'x' : '-'); - b.append(((info.mode & 0040) != 0) ? 'r' : '-'); - b.append(((info.mode & 0020) != 0) ? 'w' : '-'); - b.append(((info.mode & 0010) != 0) ? 'x' : '-'); - b.append(((info.mode & 0004) != 0) ? 'r' : '-'); - b.append(((info.mode & 0002) != 0) ? 'w' : '-'); - b.append(((info.mode & 0001) != 0) ? 'x' : '-'); - b.append(String.format(" %9d ", info.size)); - - Date stamp = new Date(info.mtime); - b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); - - b.append(info.packageName); - b.append(" :: "); - b.append(info.domain); - b.append(" :: "); - b.append(info.path); - - Slog.i(RefactoredBackupManagerService.TAG, b.toString()); - } - } - private boolean isRestorableFile(FileMetadata info) { if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { if (RefactoredBackupManagerService.MORE_DEBUG) { diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index dc477594e0ab9..7a749ea86e4a2 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -19,7 +19,6 @@ package com.android.server.backup.restore; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; import android.app.PackageInstallObserver; -import android.app.backup.BackupAgent; import android.app.backup.FullBackup; import android.app.backup.IFullBackupRestoreObserver; import android.content.pm.ApplicationInfo; @@ -45,7 +44,6 @@ import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.PasswordUtils; import com.android.server.backup.utils.TarBackupReader; -import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; @@ -55,9 +53,7 @@ import java.io.InputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.text.SimpleDateFormat; import java.util.Arrays; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; @@ -436,13 +432,13 @@ public class PerformAdbRestoreTask implements Runnable { public void onBytesRead(long bytesRead) { mBytes += bytesRead; } - }); + }, null /* monitor */); FileMetadata info; try { info = tarBackupReader.readTarHeaders(); if (info != null) { if (RefactoredBackupManagerService.MORE_DEBUG) { - dumpFileMetadata(info); + info.dump(); } final String pkg = info.packageName; @@ -469,7 +465,10 @@ public class PerformAdbRestoreTask implements Runnable { } if (info.path.equals(RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME)) { - mPackagePolicies.put(pkg, readAppManifest(info, instream)); + RestorePolicy appManifest = tarBackupReader.readAppManifest( + backupManagerService.getPackageManager(), true /* allowApks */, + mManifestSignatures, info); + mPackagePolicies.put(pkg, appManifest); mPackageInstallers.put(pkg, info.installerPackageName); // We've read only the manifest content itself at this point, // so consume the footer before looping around to the next @@ -479,7 +478,7 @@ public class PerformAdbRestoreTask implements Runnable { } else if (info.path.equals( RefactoredBackupManagerService.BACKUP_METADATA_FILENAME)) { // Metadata blobs! - readMetadata(info, instream); + tarBackupReader.readMetadata(info); tarBackupReader.skipTarPadding(info.size); } else { // Non-manifest, so it's actual file data. Is this a package @@ -835,26 +834,7 @@ public class PerformAdbRestoreTask implements Runnable { latch.await(); } - // unbind and tidy up even on timeout or failure, just in case - backupManagerService.getActivityManager().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.FIRST_APPLICATION_UID - && !app.packageName.equals("com.android.backupconfirm")) { - if (RefactoredBackupManagerService.DEBUG) { - Slog.d(RefactoredBackupManagerService.TAG, "Killing host process"); - } - backupManagerService.getActivityManager().killApplicationProcess( - app.processName, - app.uid); - } else { - if (RefactoredBackupManagerService.DEBUG) { - Slog.d(RefactoredBackupManagerService.TAG, - "Not killing after full restore"); - } - } + backupManagerService.tearDownAgentAndKill(app); } catch (RemoteException e) { Slog.d(RefactoredBackupManagerService.TAG, "Lost app trying to shut down"); } @@ -1053,250 +1033,6 @@ public class PerformAdbRestoreTask implements Runnable { return okay; } - // Read a widget metadata file, returning the restored blob - void readMetadata(FileMetadata info, InputStream instream) throws IOException { - // Fail on suspiciously large widget dump files - if (info.size > 64 * 1024) { - throw new IOException("Metadata too big; corrupt? size=" + info.size); - } - - byte[] buffer = new byte[(int) info.size]; - if (TarBackupReader.readExactly(instream, buffer, 0, (int) info.size) == info.size) { - mBytes += info.size; - } else { - throw new IOException("Unexpected EOF in widget data"); - } - - String[] str = new String[1]; - int offset = TarBackupReader.extractLine(buffer, 0, str); - int version = Integer.parseInt(str[0]); - if (version == RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION) { - offset = TarBackupReader.extractLine(buffer, offset, str); - final String pkg = str[0]; - if (info.packageName.equals(pkg)) { - // Data checks out -- the rest of the buffer is a concatenation of - // binary blobs as described in the comment at writeAppWidgetData() - ByteArrayInputStream bin = new ByteArrayInputStream(buffer, - offset, buffer.length - offset); - DataInputStream in = new DataInputStream(bin); - while (bin.available() > 0) { - int token = in.readInt(); - int size = in.readInt(); - if (size > 64 * 1024) { - throw new IOException("Datum " - + Integer.toHexString(token) - + " too big; corrupt? size=" + info.size); - } - switch (token) { - case RefactoredBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN: { - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Got widget metadata for " + info.packageName); - } - mWidgetData = new byte[size]; - in.read(mWidgetData); - break; - } - default: { - if (RefactoredBackupManagerService.DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, "Ignoring metadata blob " - + Integer.toHexString(token) - + " for " + info.packageName); - } - in.skipBytes(size); - break; - } - } - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, - "Metadata mismatch: package " + info.packageName - + " but widget data for " + pkg); - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, "Unsupported metadata version " + version); - } - } - - // Returns a policy constant; takes a buffer arg to reduce memory churn - RestorePolicy readAppManifest(FileMetadata info, InputStream instream) - throws IOException { - // Fail on suspiciously large manifest files - if (info.size > 64 * 1024) { - throw new IOException("Restore manifest too big; corrupt? size=" + info.size); - } - - byte[] buffer = new byte[(int) info.size]; - if (TarBackupReader.readExactly(instream, buffer, 0, (int) info.size) == info.size) { - mBytes += info.size; - } else { - throw new IOException("Unexpected EOF in manifest"); - } - - RestorePolicy policy = RestorePolicy.IGNORE; - String[] str = new String[1]; - int offset = 0; - - try { - offset = TarBackupReader.extractLine(buffer, offset, str); - int version = Integer.parseInt(str[0]); - if (version == RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION) { - offset = TarBackupReader.extractLine(buffer, offset, str); - String manifestPackage = str[0]; - // TODO: handle - if (manifestPackage.equals(info.packageName)) { - offset = TarBackupReader.extractLine(buffer, offset, str); - version = Integer.parseInt(str[0]); // app version - offset = TarBackupReader.extractLine(buffer, offset, str); - // This is the platform version, which we don't use, but we parse it - // as a safety against corruption in the manifest. - Integer.parseInt(str[0]); - offset = TarBackupReader.extractLine(buffer, offset, str); - info.installerPackageName = (str[0].length() > 0) ? str[0] : null; - offset = TarBackupReader.extractLine(buffer, offset, str); - boolean hasApk = str[0].equals("1"); - offset = TarBackupReader.extractLine(buffer, offset, str); - int numSigs = Integer.parseInt(str[0]); - if (numSigs > 0) { - Signature[] sigs = new Signature[numSigs]; - for (int i = 0; i < numSigs; i++) { - offset = TarBackupReader.extractLine(buffer, offset, str); - sigs[i] = new Signature(str[0]); - } - mManifestSignatures.put(info.packageName, sigs); - - // Okay, got the manifest info we need... - try { - PackageInfo pkgInfo = - backupManagerService.getPackageManager().getPackageInfo( - info.packageName, PackageManager.GET_SIGNATURES); - // Fall through to IGNORE if the app explicitly disallows backup - final int flags = pkgInfo.applicationInfo.flags; - if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { - // Restore system-uid-space packages only if they have - // defined a custom backup agent - if ((pkgInfo.applicationInfo.uid - >= Process.FIRST_APPLICATION_UID) - || (pkgInfo.applicationInfo.backupAgentName != null)) { - // Verify signatures against any installed version; if they - // don't match, then we fall though and ignore the data. The - // signatureMatch() method explicitly ignores the signature - // check for packages installed on the system partition, because - // such packages are signed with the platform cert instead of - // the app developer's cert, so they're different on every - // device. - if (AppBackupUtils.signaturesMatch(sigs, - pkgInfo)) { - if ((pkgInfo.applicationInfo.flags - & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) { - Slog.i(RefactoredBackupManagerService.TAG, - "Package has restoreAnyVersion; taking data"); - policy = RestorePolicy.ACCEPT; - } else if (pkgInfo.versionCode >= version) { - Slog.i(RefactoredBackupManagerService.TAG, - "Sig + version match; taking data"); - policy = RestorePolicy.ACCEPT; - } else { - // The data is from a newer version of the app than - // is presently installed. That means we can only - // use it if the matching apk is also supplied. - Slog.d(RefactoredBackupManagerService.TAG, - "Data version " + version - + " is newer than installed version " - + pkgInfo.versionCode - + " - requiring apk"); - policy = RestorePolicy.ACCEPT_IF_APK; - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, - "Restore manifest signatures do not match " - + "installed application for " - + info.packageName); - } - } else { - Slog.w(RefactoredBackupManagerService.TAG, - "Package " + info.packageName - + " is system level with no agent"); - } - } else { - if (RefactoredBackupManagerService.DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Restore manifest from " - + info.packageName + " but allowBackup=false"); - } - } - } catch (NameNotFoundException e) { - // Okay, the target app isn't installed. We can process - // the restore properly only if the dataset provides the - // apk file and we can successfully install it. - if (RefactoredBackupManagerService.DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Package " + info.packageName - + " not installed; requiring apk in dataset"); - } - policy = RestorePolicy.ACCEPT_IF_APK; - } - - if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { - Slog.i(RefactoredBackupManagerService.TAG, - "Cannot restore package " + info.packageName - + " without the matching .apk"); - } - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Missing signature on backed-up package " - + info.packageName); - } - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Expected package " + info.packageName - + " but restore manifest claims " + manifestPackage); - } - } else { - Slog.i(RefactoredBackupManagerService.TAG, - "Unknown restore manifest version " + version - + " for package " + info.packageName); - } - } catch (NumberFormatException e) { - Slog.w(RefactoredBackupManagerService.TAG, - "Corrupt restore manifest for package " + info.packageName); - } catch (IllegalArgumentException e) { - Slog.w(RefactoredBackupManagerService.TAG, e.getMessage()); - } - - return policy; - } - - void dumpFileMetadata(FileMetadata info) { - if (RefactoredBackupManagerService.DEBUG) { - StringBuilder b = new StringBuilder(128); - - // mode string - b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); - b.append(((info.mode & 0400) != 0) ? 'r' : '-'); - b.append(((info.mode & 0200) != 0) ? 'w' : '-'); - b.append(((info.mode & 0100) != 0) ? 'x' : '-'); - b.append(((info.mode & 0040) != 0) ? 'r' : '-'); - b.append(((info.mode & 0020) != 0) ? 'w' : '-'); - b.append(((info.mode & 0010) != 0) ? 'x' : '-'); - b.append(((info.mode & 0004) != 0) ? 'r' : '-'); - b.append(((info.mode & 0002) != 0) ? 'w' : '-'); - b.append(((info.mode & 0001) != 0) ? 'x' : '-'); - b.append(String.format(" %9d ", info.size)); - - Date stamp = new Date(info.mtime); - b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); - - b.append(info.packageName); - b.append(" :: "); - b.append(info.domain); - b.append(" :: "); - b.append(info.path); - - Slog.i(RefactoredBackupManagerService.TAG, b.toString()); - } - } - void sendStartRestore() { if (mObserver != null) { try { diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 643936e11b0df..714b664ba843b 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -500,10 +500,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { + " > installed version " + mCurrentPackage.versionCode; Slog.w(RefactoredBackupManagerService.TAG, "Package " + pkgName + ": " + message); - Bundle monitoringExtras = backupManagerService.putMonitoringExtra(null, + Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION, metaInfo.versionCode); - monitoringExtras = backupManagerService.putMonitoringExtra(monitoringExtras, + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY, false); mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER, @@ -521,10 +521,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { + " > installed version " + mCurrentPackage.versionCode + " but restoreAnyVersion"); } - Bundle monitoringExtras = backupManagerService.putMonitoringExtra(null, + Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION, metaInfo.versionCode); - monitoringExtras = backupManagerService.putMonitoringExtra(monitoringExtras, + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY, true); mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER, diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java index 447962cb4ec45..53addfc5e412a 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java +++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java @@ -53,4 +53,36 @@ public class BackupManagerMonitorUtils { } return null; } + + public static Bundle putMonitoringExtra(Bundle extras, String key, String value) { + if (extras == null) { + extras = new Bundle(); + } + extras.putString(key, value); + return extras; + } + + private static Bundle putMonitoringExtra(Bundle extras, String key, int value) { + if (extras == null) { + extras = new Bundle(); + } + extras.putInt(key, value); + return extras; + } + + public static Bundle putMonitoringExtra(Bundle extras, String key, long value) { + if (extras == null) { + extras = new Bundle(); + } + extras.putLong(key, value); + return extras; + } + + public static Bundle putMonitoringExtra(Bundle extras, String key, boolean value) { + if (extras == null) { + extras = new Bundle(); + } + extras.putBoolean(key, value); + return extras; + } } diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java index bd7579f8911c7..4102225a3e287 100644 --- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java +++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java @@ -1,14 +1,44 @@ package com.android.server.backup.utils; +import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME; +import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION; +import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_MANIFEST_PACKAGE_NAME; +import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OLD_VERSION; +import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_POLICY_ALLOW_APKS; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH; +import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER; + import android.app.backup.BackupAgent; +import android.app.backup.BackupManagerMonitor; import android.app.backup.FullBackup; +import android.app.backup.IBackupManagerMonitor; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.os.Bundle; +import android.os.Process; import android.util.Slog; import com.android.server.backup.FileMetadata; import com.android.server.backup.RefactoredBackupManagerService; +import com.android.server.backup.restore.RestorePolicy; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; /** * Utility methods to read backup tar file. @@ -30,6 +60,11 @@ public class TarBackupReader { private final InputStream mInputStream; private final BytesReadListener mBytesReadListener; + private IBackupManagerMonitor mMonitor; + + // Widget blob to be restored out-of-band. + private byte[] mWidgetData = null; + /** * Listener for bytes reading. */ @@ -37,75 +72,11 @@ public class TarBackupReader { void onBytesRead(long bytesRead); } - public TarBackupReader(InputStream inputStream, BytesReadListener bytesReadListener) { + public TarBackupReader(InputStream inputStream, BytesReadListener bytesReadListener, + IBackupManagerMonitor monitor) { mInputStream = inputStream; mBytesReadListener = bytesReadListener; - } - - /** - * Tries to read exactly the given number of bytes into a buffer at the stated offset. - * - * @param in - input stream to read bytes from.. - * @param buffer - where to write bytes to. - * @param offset - offset in buffer to write bytes to. - * @param size - number of bytes to read. - * @return number of bytes actually read. - * @throws IOException in case of an error. - */ - public static int readExactly(InputStream in, byte[] buffer, int offset, int size) - throws IOException { - if (size <= 0) { - throw new IllegalArgumentException("size must be > 0"); - } - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, " ... readExactly(" + size + ") called"); - } - int soFar = 0; - while (soFar < size) { - int nRead = in.read(buffer, offset + soFar, size - soFar); - if (nRead <= 0) { - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.w(RefactoredBackupManagerService.TAG, - "- wanted exactly " + size + " but got only " + soFar); - } - break; - } - soFar += nRead; - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.v(RefactoredBackupManagerService.TAG, - " + got " + nRead + "; now wanting " + (size - soFar)); - } - } - return soFar; - } - - /** - * Builds a line from a byte buffer starting at 'offset'. - * - * @param buffer - where to read a line from. - * @param offset - offset in buffer to read a line from. - * @param outStr - an output parameter, the result will be put in outStr. - * @return the index of the next unconsumed data in the buffer. - * @throws IOException in case of an error. - */ - public static int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { - final int end = buffer.length; - if (offset >= end) { - throw new IOException("Incomplete data"); - } - - int pos; - for (pos = offset; pos < end; pos++) { - byte c = buffer[pos]; - // at LF we declare end of line, and return the next char as the - // starting point for the next time through - if (c == '\n') { - break; - } - } - outStr[0] = new String(buffer, offset, pos - offset); - pos++; // may be pointing an extra byte past the end but that's okay - return pos; + mMonitor = monitor; } /** @@ -247,6 +218,454 @@ public class TarBackupReader { return info; } + /** + * Tries to read exactly the given number of bytes into a buffer at the stated offset. + * + * @param in - input stream to read bytes from.. + * @param buffer - where to write bytes to. + * @param offset - offset in buffer to write bytes to. + * @param size - number of bytes to read. + * @return number of bytes actually read. + * @throws IOException in case of an error. + */ + private static int readExactly(InputStream in, byte[] buffer, int offset, int size) + throws IOException { + if (size <= 0) { + throw new IllegalArgumentException("size must be > 0"); + } + if (RefactoredBackupManagerService.MORE_DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, " ... readExactly(" + size + ") called"); + } + int soFar = 0; + while (soFar < size) { + int nRead = in.read(buffer, offset + soFar, size - soFar); + if (nRead <= 0) { + if (RefactoredBackupManagerService.MORE_DEBUG) { + Slog.w(RefactoredBackupManagerService.TAG, + "- wanted exactly " + size + " but got only " + soFar); + } + break; + } + soFar += nRead; + if (RefactoredBackupManagerService.MORE_DEBUG) { + Slog.v(RefactoredBackupManagerService.TAG, + " + got " + nRead + "; now wanting " + (size - soFar)); + } + } + return soFar; + } + + /** + * Reads app manifest and returns a policy constant. + * + * @param packageManager - PackageManager instance. + * @param allowApks - allow restore set to include apks. + * @param manifestSignatures - parsed signatures will be put here. + * @param info - file metadata. + * @return a policy constant. + * @throws IOException in case of an error. + */ + public RestorePolicy readAppManifest(PackageManager packageManager, + boolean allowApks, HashMap manifestSignatures, + FileMetadata info) + throws IOException { + // Fail on suspiciously large manifest files + if (info.size > 64 * 1024) { + throw new IOException("Restore manifest too big; corrupt? size=" + info.size); + } + + byte[] buffer = new byte[(int) info.size]; + if (RefactoredBackupManagerService.MORE_DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, + " readAppManifest() looking for " + info.size + " bytes"); + } + if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) { + mBytesReadListener.onBytesRead(info.size); + } else { + throw new IOException("Unexpected EOF in manifest"); + } + + RestorePolicy policy = RestorePolicy.IGNORE; + String[] str = new String[1]; + int offset = 0; + + try { + offset = extractLine(buffer, offset, str); + int version = Integer.parseInt(str[0]); + if (version == RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION) { + offset = extractLine(buffer, offset, str); + String manifestPackage = str[0]; + // TODO: handle + if (manifestPackage.equals(info.packageName)) { + offset = extractLine(buffer, offset, str); + version = Integer.parseInt(str[0]); // app version + offset = extractLine(buffer, offset, str); + // This is the platform version, which we don't use, but we parse it + // as a safety against corruption in the manifest. + Integer.parseInt(str[0]); + offset = extractLine(buffer, offset, str); + info.installerPackageName = (str[0].length() > 0) ? str[0] : null; + offset = extractLine(buffer, offset, str); + boolean hasApk = str[0].equals("1"); + offset = extractLine(buffer, offset, str); + int numSigs = Integer.parseInt(str[0]); + if (numSigs > 0) { + Signature[] sigs = new Signature[numSigs]; + for (int i = 0; i < numSigs; i++) { + offset = extractLine(buffer, offset, str); + sigs[i] = new Signature(str[0]); + } + manifestSignatures.put(info.packageName, sigs); + + // Okay, got the manifest info we need... + try { + PackageInfo pkgInfo = packageManager.getPackageInfo( + info.packageName, PackageManager.GET_SIGNATURES); + // Fall through to IGNORE if the app explicitly disallows backup + final int flags = pkgInfo.applicationInfo.flags; + if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { + // Restore system-uid-space packages only if they have + // defined a custom backup agent + if ((pkgInfo.applicationInfo.uid + >= Process.FIRST_APPLICATION_UID) + || (pkgInfo.applicationInfo.backupAgentName != null)) { + // Verify signatures against any installed version; if they + // don't match, then we fall though and ignore the data. The + // signatureMatch() method explicitly ignores the signature + // check for packages installed on the system partition, because + // such packages are signed with the platform cert instead of + // the app developer's cert, so they're different on every + // device. + if (AppBackupUtils.signaturesMatch(sigs, + pkgInfo)) { + if ((pkgInfo.applicationInfo.flags + & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) { + Slog.i(RefactoredBackupManagerService.TAG, + "Package has restoreAnyVersion; taking data"); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_RESTORE_ANY_VERSION, + pkgInfo, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + null); + policy = RestorePolicy.ACCEPT; + } else if (pkgInfo.versionCode >= version) { + Slog.i(RefactoredBackupManagerService.TAG, + "Sig + version match; taking data"); + policy = RestorePolicy.ACCEPT; + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_VERSIONS_MATCH, + pkgInfo, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + null); + } else { + // The data is from a newer version of the app than + // is presently installed. That means we can only + // use it if the matching apk is also supplied. + if (allowApks) { + Slog.i(RefactoredBackupManagerService.TAG, + "Data version " + version + + " is newer than installed " + + "version " + + pkgInfo.versionCode + + " - requiring apk"); + policy = RestorePolicy.ACCEPT_IF_APK; + } else { + Slog.i(RefactoredBackupManagerService.TAG, + "Data requires newer version " + + version + "; ignoring"); + mMonitor = BackupManagerMonitorUtils + .monitorEvent(mMonitor, + LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER, + pkgInfo, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + BackupManagerMonitorUtils + .putMonitoringExtra( + null, + EXTRA_LOG_OLD_VERSION, + version)); + + policy = RestorePolicy.IGNORE; + } + } + } else { + Slog.w(RefactoredBackupManagerService.TAG, + "Restore manifest signatures do not match " + + "installed application for " + + info.packageName); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH, + pkgInfo, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + null); + } + } else { + Slog.w(RefactoredBackupManagerService.TAG, + "Package " + info.packageName + + " is system level with no agent"); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + + + mMonitor, + LOG_EVENT_ID_SYSTEM_APP_NO_AGENT, + pkgInfo, + LOG_EVENT_CATEGORY_AGENT, + null); + } + } else { + if (RefactoredBackupManagerService.DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, + "Restore manifest from " + + info.packageName + " but allowBackup=false"); + } + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE, + pkgInfo, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + null); + } + } catch (PackageManager.NameNotFoundException e) { + // Okay, the target app isn't installed. We can process + // the restore properly only if the dataset provides the + // apk file and we can successfully install it. + if (allowApks) { + if (RefactoredBackupManagerService.DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, + "Package " + info.packageName + + " not installed; requiring apk in dataset"); + } + policy = RestorePolicy.ACCEPT_IF_APK; + } else { + policy = RestorePolicy.IGNORE; + } + Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra( + null, + EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra( + monitoringExtras, + EXTRA_LOG_POLICY_ALLOW_APKS, allowApks); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_APK_NOT_INSTALLED, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + } + + if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { + Slog.i(RefactoredBackupManagerService.TAG, + "Cannot restore package " + info.packageName + + " without the matching .apk"); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + BackupManagerMonitorUtils.putMonitoringExtra(null, + EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); + } + } else { + Slog.i(RefactoredBackupManagerService.TAG, + "Missing signature on backed-up package " + + info.packageName); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_MISSING_SIGNATURE, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + BackupManagerMonitorUtils.putMonitoringExtra(null, + EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); + } + } else { + Slog.i(RefactoredBackupManagerService.TAG, + "Expected package " + info.packageName + + " but restore manifest claims " + manifestPackage); + Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, + EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra( + monitoringExtras, + EXTRA_LOG_MANIFEST_PACKAGE_NAME, manifestPackage); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + } + } else { + Slog.i(RefactoredBackupManagerService.TAG, + "Unknown restore manifest version " + version + + " for package " + info.packageName); + Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, + EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, + EXTRA_LOG_EVENT_PACKAGE_VERSION, version); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + + } + } catch (NumberFormatException e) { + Slog.w(RefactoredBackupManagerService.TAG, + "Corrupt restore manifest for package " + info.packageName); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + BackupManagerMonitorUtils.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, + info.packageName)); + } catch (IllegalArgumentException e) { + Slog.w(RefactoredBackupManagerService.TAG, e.getMessage()); + } + + return policy; + } + + // Given an actual file content size, consume the post-content padding mandated + // by the tar format. + public void skipTarPadding(long size) throws IOException { + long partial = (size + 512) % 512; + if (partial > 0) { + final int needed = 512 - (int) partial; + if (RefactoredBackupManagerService.MORE_DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, + "Skipping tar padding: " + needed + " bytes"); + } + byte[] buffer = new byte[needed]; + if (readExactly(mInputStream, buffer, 0, needed) == needed) { + mBytesReadListener.onBytesRead(needed); + } else { + throw new IOException("Unexpected EOF in padding"); + } + } + } + + /** + * Read a widget metadata file, returning the restored blob. + */ + public void readMetadata(FileMetadata info) throws IOException { + // Fail on suspiciously large widget dump files + if (info.size > 64 * 1024) { + throw new IOException("Metadata too big; corrupt? size=" + info.size); + } + + byte[] buffer = new byte[(int) info.size]; + if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) { + mBytesReadListener.onBytesRead(info.size); + } else { + throw new IOException("Unexpected EOF in widget data"); + } + + String[] str = new String[1]; + int offset = extractLine(buffer, 0, str); + int version = Integer.parseInt(str[0]); + if (version == RefactoredBackupManagerService.BACKUP_MANIFEST_VERSION) { + offset = extractLine(buffer, offset, str); + final String pkg = str[0]; + if (info.packageName.equals(pkg)) { + // Data checks out -- the rest of the buffer is a concatenation of + // binary blobs as described in the comment at writeAppWidgetData() + ByteArrayInputStream bin = new ByteArrayInputStream(buffer, + offset, buffer.length - offset); + DataInputStream in = new DataInputStream(bin); + while (bin.available() > 0) { + int token = in.readInt(); + int size = in.readInt(); + if (size > 64 * 1024) { + throw new IOException("Datum " + + Integer.toHexString(token) + + " too big; corrupt? size=" + info.size); + } + switch (token) { + case RefactoredBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN: { + if (RefactoredBackupManagerService.MORE_DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, + "Got widget metadata for " + info.packageName); + } + mWidgetData = new byte[size]; + in.read(mWidgetData); + break; + } + default: { + if (RefactoredBackupManagerService.DEBUG) { + Slog.i(RefactoredBackupManagerService.TAG, "Ignoring metadata blob " + + Integer.toHexString(token) + + " for " + info.packageName); + } + in.skipBytes(size); + break; + } + } + } + } else { + Slog.w(RefactoredBackupManagerService.TAG, + "Metadata mismatch: package " + info.packageName + + " but widget data for " + pkg); + + Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, + EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, + BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + } + } else { + Slog.w(RefactoredBackupManagerService.TAG, "Unsupported metadata version " + version); + + Bundle monitoringExtras = BackupManagerMonitorUtils + .putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, + info.packageName); + monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, + EXTRA_LOG_EVENT_PACKAGE_VERSION, version); + mMonitor = BackupManagerMonitorUtils.monitorEvent( + mMonitor, + BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION, + null, + LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, + monitoringExtras); + } + } + + /** + * Builds a line from a byte buffer starting at 'offset'. + * + * @param buffer - where to read a line from. + * @param offset - offset in buffer to read a line from. + * @param outStr - an output parameter, the result will be put in outStr. + * @return the index of the next unconsumed data in the buffer. + * @throws IOException in case of an error. + */ + private static int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { + final int end = buffer.length; + if (offset >= end) { + throw new IOException("Incomplete data"); + } + + int pos; + for (pos = offset; pos < end; pos++) { + byte c = buffer[pos]; + // at LF we declare end of line, and return the next char as the + // starting point for the next time through + if (c == '\n') { + break; + } + } + outStr[0] = new String(buffer, offset, pos - offset); + pos++; // may be pointing an extra byte past the end but that's okay + return pos; + } + private boolean readTarHeader(byte[] block) throws IOException { final int got = readExactly(mInputStream, block, 0, 512); if (got == 0) { @@ -323,25 +742,6 @@ public class TarBackupReader { return true; } - // Given an actual file content size, consume the post-content padding mandated - // by the tar format. - public void skipTarPadding(long size) throws IOException { - long partial = (size + 512) % 512; - if (partial > 0) { - final int needed = 512 - (int) partial; - if (RefactoredBackupManagerService.MORE_DEBUG) { - Slog.i(RefactoredBackupManagerService.TAG, - "Skipping tar padding: " + needed + " bytes"); - } - byte[] buffer = new byte[needed]; - if (readExactly(mInputStream, buffer, 0, needed) == needed) { - mBytesReadListener.onBytesRead(needed); - } else { - throw new IOException("Unexpected EOF in padding"); - } - } - } - private static long extractRadix(byte[] data, int offset, int maxChars, int radix) throws IOException { long value = 0; @@ -388,4 +788,11 @@ public class TarBackupReader { } } + public IBackupManagerMonitor getMonitor() { + return mMonitor; + } + + public byte[] getWidgetData() { + return mWidgetData; + } }