diff options
18 files changed, 530 insertions, 387 deletions
diff --git a/api/current.txt b/api/current.txt index d009a64..d7e3cb3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -183,14 +183,14 @@ package android { public static final class R.attr { ctor public R.attr(); field public static final int absListViewStyle = 16842858; // 0x101006a - field public static final int accessibilityEventTypes = 16843650; // 0x1010382 - field public static final int accessibilityFeedbackType = 16843652; // 0x1010384 - field public static final int accessibilityFlags = 16843654; // 0x1010386 + field public static final int accessibilityEventTypes = 16843649; // 0x1010381 + field public static final int accessibilityFeedbackType = 16843651; // 0x1010383 + field public static final int accessibilityFlags = 16843653; // 0x1010385 field public static final int accountPreferences = 16843423; // 0x101029f field public static final int accountType = 16843407; // 0x101028f field public static final int action = 16842797; // 0x101002d field public static final int actionBarSize = 16843499; // 0x10102eb - field public static final int actionBarSplitStyle = 16843676; // 0x101039c + field public static final int actionBarSplitStyle = 16843675; // 0x101039b field public static final int actionBarStyle = 16843470; // 0x10102ce field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4 field public static final int actionBarTabStyle = 16843507; // 0x10102f3 @@ -206,9 +206,9 @@ package android { field public static final int actionModeCopyDrawable = 16843538; // 0x1010312 field public static final int actionModeCutDrawable = 16843537; // 0x1010311 field public static final int actionModePasteDrawable = 16843539; // 0x1010313 - field public static final int actionModeSelectAllDrawable = 16843648; // 0x1010380 + field public static final int actionModeSelectAllDrawable = 16843647; // 0x101037f field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6 - field public static final int actionProviderClass = 16843678; // 0x101039e + field public static final int actionProviderClass = 16843677; // 0x101039d field public static final int actionViewClass = 16843516; // 0x10102fc field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba @@ -220,7 +220,7 @@ package android { field public static final int alertDialogIcon = 16843605; // 0x1010355 field public static final int alertDialogStyle = 16842845; // 0x101005d field public static final int alertDialogTheme = 16843529; // 0x1010309 - field public static final int alignmentMode = 16843642; // 0x101037a + field public static final int alignmentMode = 16843641; // 0x1010379 field public static final int allContactsName = 16843468; // 0x10102cc field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 @@ -262,7 +262,7 @@ package android { field public static final int borderlessButtonStyle = 16843563; // 0x101032b field public static final int bottom = 16843184; // 0x10101b0 field public static final int bottomBright = 16842957; // 0x10100cd - field public static final int bottomChevronDrawable = 16843661; // 0x101038d + field public static final int bottomChevronDrawable = 16843660; // 0x101038c field public static final int bottomDark = 16842953; // 0x10100c9 field public static final int bottomLeftRadius = 16843179; // 0x10101ab field public static final int bottomMedium = 16842958; // 0x10100ce @@ -281,7 +281,7 @@ package android { field public static final int cacheColorHint = 16843009; // 0x1010101 field public static final int calendarViewShown = 16843596; // 0x101034c field public static final int calendarViewStyle = 16843613; // 0x101035d - field public static final int canRetrieveWindowContent = 16843655; // 0x1010387 + field public static final int canRetrieveWindowContent = 16843654; // 0x1010386 field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final deprecated int capitalize = 16843113; // 0x1010169 field public static final int centerBright = 16842956; // 0x10100cc @@ -314,9 +314,9 @@ package android { field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab field public static final int colorForeground = 16842800; // 0x1010030 field public static final int colorForegroundInverse = 16843270; // 0x1010206 - field public static final int columnCount = 16843639; // 0x1010377 + field public static final int columnCount = 16843638; // 0x1010376 field public static final int columnDelay = 16843215; // 0x10101cf - field public static final int columnOrderPreserved = 16843640; // 0x1010378 + field public static final int columnOrderPreserved = 16843639; // 0x1010377 field public static final int columnWidth = 16843031; // 0x1010117 field public static final int compatibleWidthLimitDp = 16843621; // 0x1010365 field public static final int completionHint = 16843122; // 0x1010172 @@ -429,7 +429,7 @@ package android { field public static final int fastScrollTextColor = 16843609; // 0x1010359 field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336 field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339 - field public static final int feedbackCount = 16843667; // 0x1010393 + field public static final int feedbackCount = 16843666; // 0x1010392 field public static final int fillAfter = 16843197; // 0x10101bd field public static final int fillBefore = 16843196; // 0x10101bc field public static final int fillEnabled = 16843343; // 0x101024f @@ -462,7 +462,6 @@ package android { field public static final int fromXScale = 16843202; // 0x10101c2 field public static final int fromYDelta = 16843208; // 0x10101c8 field public static final int fromYScale = 16843204; // 0x10101c4 - field public static final int fullBackupAgent = 16843635; // 0x1010373 field public static final int fullBright = 16842954; // 0x10100ca field public static final int fullDark = 16842950; // 0x10100c6 field public static final int functionalTest = 16842787; // 0x1010023 @@ -483,7 +482,7 @@ package android { field public static final int hand_hour = 16843011; // 0x1010103 field public static final int hand_minute = 16843012; // 0x1010104 field public static final int handle = 16843354; // 0x101025a - field public static final int handleDrawable = 16843657; // 0x1010389 + field public static final int handleDrawable = 16843656; // 0x1010388 field public static final int handleProfiling = 16842786; // 0x1010022 field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e field public static final int hardwareAccelerated = 16843475; // 0x10102d3 @@ -492,12 +491,12 @@ package android { field public static final int headerDividersEnabled = 16843310; // 0x101022e field public static final int height = 16843093; // 0x1010155 field public static final int hint = 16843088; // 0x1010150 - field public static final int hitRadius = 16843664; // 0x1010390 + field public static final int hitRadius = 16843663; // 0x101038f field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d field public static final int horizontalDivider = 16843053; // 0x101012d field public static final int horizontalGap = 16843327; // 0x101023f - field public static final int horizontalOffset = 16843669; // 0x1010395 + field public static final int horizontalOffset = 16843668; // 0x1010394 field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353 field public static final int horizontalSpacing = 16843028; // 0x1010114 field public static final int host = 16842792; // 0x1010028 @@ -543,7 +542,7 @@ package android { field public static final int installLocation = 16843447; // 0x10102b7 field public static final int interpolator = 16843073; // 0x1010141 field public static final int isAlwaysSyncable = 16843571; // 0x1010333 - field public static final int isAuxiliary = 16843649; // 0x1010381 + field public static final int isAuxiliary = 16843648; // 0x1010380 field public static final int isDefault = 16843297; // 0x1010221 field public static final int isIndicator = 16843079; // 0x1010147 field public static final int isModifier = 16843334; // 0x1010246 @@ -597,30 +596,30 @@ package android { field public static final int layout_centerInParent = 16843151; // 0x101018f field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c - field public static final int layout_columnSpan = 16843645; // 0x101037d + field public static final int layout_columnSpan = 16843644; // 0x101037c field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 - field public static final int layout_heightSpec = 16843647; // 0x101037f + field public static final int layout_heightSpec = 16843646; // 0x101037e field public static final int layout_margin = 16842998; // 0x10100f6 field public static final int layout_marginBottom = 16843002; // 0x10100fa - field public static final int layout_marginEnd = 16843675; // 0x101039b + field public static final int layout_marginEnd = 16843674; // 0x101039a field public static final int layout_marginLeft = 16842999; // 0x10100f7 field public static final int layout_marginRight = 16843001; // 0x10100f9 - field public static final int layout_marginStart = 16843674; // 0x101039a + field public static final int layout_marginStart = 16843673; // 0x1010399 field public static final int layout_marginTop = 16843000; // 0x10100f8 - field public static final int layout_row = 16843643; // 0x101037b - field public static final int layout_rowSpan = 16843644; // 0x101037c + field public static final int layout_row = 16843642; // 0x101037a + field public static final int layout_rowSpan = 16843643; // 0x101037b field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toLeftOf = 16843138; // 0x1010182 field public static final int layout_toRightOf = 16843139; // 0x1010183 field public static final int layout_weight = 16843137; // 0x1010181 field public static final int layout_width = 16842996; // 0x10100f4 - field public static final int layout_widthSpec = 16843646; // 0x101037e + field public static final int layout_widthSpec = 16843645; // 0x101037d field public static final int layout_x = 16843135; // 0x101017f field public static final int layout_y = 16843136; // 0x1010180 field public static final int left = 16843181; // 0x10101ad - field public static final int leftChevronDrawable = 16843658; // 0x101038a + field public static final int leftChevronDrawable = 16843657; // 0x1010389 field public static final int lineSpacingExtra = 16843287; // 0x1010217 field public static final int lineSpacingMultiplier = 16843288; // 0x1010218 field public static final int lines = 16843092; // 0x1010154 @@ -632,8 +631,8 @@ package android { field public static final int listDividerAlertDialog = 16843525; // 0x1010305 field public static final int listPopupWindowStyle = 16843519; // 0x10102ff field public static final int listPreferredItemHeight = 16842829; // 0x101004d - field public static final int listPreferredItemHeightLarge = 16843670; // 0x1010396 - field public static final int listPreferredItemHeightSmall = 16843671; // 0x1010397 + field public static final int listPreferredItemHeightLarge = 16843669; // 0x1010395 + field public static final int listPreferredItemHeightSmall = 16843670; // 0x1010396 field public static final int listSelector = 16843003; // 0x10100fb field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208 field public static final int listViewStyle = 16842868; // 0x1010074 @@ -679,7 +678,7 @@ package android { field public static final int nextFocusUp = 16842979; // 0x10100e3 field public static final int noHistory = 16843309; // 0x101022d field public static final int normalScreens = 16843397; // 0x1010285 - field public static final int notificationTimeout = 16843653; // 0x1010385 + field public static final int notificationTimeout = 16843652; // 0x1010384 field public static final int numColumns = 16843032; // 0x1010118 field public static final int numStars = 16843076; // 0x1010144 field public static final deprecated int numeric = 16843109; // 0x1010165 @@ -693,17 +692,17 @@ package android { field public static final int orderingFromXml = 16843239; // 0x10101e7 field public static final int orientation = 16842948; // 0x10100c4 field public static final int outAnimation = 16843128; // 0x1010178 - field public static final int outerRadius = 16843663; // 0x101038f + field public static final int outerRadius = 16843662; // 0x101038e field public static final int overScrollFooter = 16843459; // 0x10102c3 field public static final int overScrollHeader = 16843458; // 0x10102c2 field public static final int overScrollMode = 16843457; // 0x10102c1 - field public static final int packageNames = 16843651; // 0x1010383 + field public static final int packageNames = 16843650; // 0x1010382 field public static final int padding = 16842965; // 0x10100d5 field public static final int paddingBottom = 16842969; // 0x10100d9 - field public static final int paddingEnd = 16843673; // 0x1010399 + field public static final int paddingEnd = 16843672; // 0x1010398 field public static final int paddingLeft = 16842966; // 0x10100d6 field public static final int paddingRight = 16842968; // 0x10100d8 - field public static final int paddingStart = 16843672; // 0x1010398 + field public static final int paddingStart = 16843671; // 0x1010397 field public static final int paddingTop = 16842967; // 0x10100d7 field public static final int panelBackground = 16842846; // 0x101005e field public static final int panelColorBackground = 16842849; // 0x1010061 @@ -784,17 +783,17 @@ package android { field public static final int restoreAnyVersion = 16843450; // 0x10102ba field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d field public static final int right = 16843183; // 0x10101af - field public static final int rightChevronDrawable = 16843659; // 0x101038b + field public static final int rightChevronDrawable = 16843658; // 0x101038a field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 field public static final int rotation = 16843558; // 0x1010326 field public static final int rotationX = 16843559; // 0x1010327 field public static final int rotationY = 16843560; // 0x1010328 - field public static final int rowCount = 16843637; // 0x1010375 + field public static final int rowCount = 16843636; // 0x1010374 field public static final int rowDelay = 16843216; // 0x10101d0 field public static final int rowEdgeFlags = 16843329; // 0x1010241 field public static final int rowHeight = 16843058; // 0x1010132 - field public static final int rowOrderPreserved = 16843638; // 0x1010376 + field public static final int rowOrderPreserved = 16843637; // 0x1010375 field public static final int saveEnabled = 16842983; // 0x10100e7 field public static final int scaleGravity = 16843262; // 0x10101fe field public static final int scaleHeight = 16843261; // 0x10101fd @@ -860,7 +859,7 @@ package android { field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 - field public static final int snapMargin = 16843666; // 0x1010392 + field public static final int snapMargin = 16843665; // 0x1010391 field public static final int soundEffectsEnabled = 16843285; // 0x1010215 field public static final int spacing = 16843027; // 0x1010113 field public static final int spinnerDropDownItemStyle = 16842887; // 0x1010087 @@ -908,7 +907,7 @@ package android { field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int suggestActionMsg = 16843228; // 0x10101dc field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd - field public static final int suggestionsEnabled = 16843636; // 0x1010374 + field public static final int suggestionsEnabled = 16843635; // 0x1010373 field public static final int summary = 16843241; // 0x10101e9 field public static final int summaryColumn = 16843426; // 0x10102a2 field public static final int summaryOff = 16843248; // 0x10101f0 @@ -925,7 +924,7 @@ package android { field public static final int tag = 16842961; // 0x10100d1 field public static final int targetActivity = 16843266; // 0x1010202 field public static final int targetClass = 16842799; // 0x101002f - field public static final int targetDrawables = 16843656; // 0x1010388 + field public static final int targetDrawables = 16843655; // 0x1010387 field public static final int targetPackage = 16842785; // 0x1010021 field public static final int targetSdkVersion = 16843376; // 0x1010270 field public static final int taskAffinity = 16842770; // 0x1010012 @@ -976,7 +975,7 @@ package android { field public static final int textColorTertiary = 16843282; // 0x1010212 field public static final int textColorTertiaryInverse = 16843283; // 0x1010213 field public static final int textCursorDrawable = 16843618; // 0x1010362 - field public static final int textDirection = 16843677; // 0x101039d + field public static final int textDirection = 16843676; // 0x101039c field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315 field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314 field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f @@ -1016,7 +1015,7 @@ package android { field public static final int toYScale = 16843205; // 0x10101c5 field public static final int top = 16843182; // 0x10101ae field public static final int topBright = 16842955; // 0x10100cb - field public static final int topChevronDrawable = 16843660; // 0x101038c + field public static final int topChevronDrawable = 16843659; // 0x101038b field public static final int topDark = 16842951; // 0x10100c7 field public static final int topLeftRadius = 16843177; // 0x10101a9 field public static final int topOffset = 16843352; // 0x1010258 @@ -1032,7 +1031,7 @@ package android { field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 - field public static final int useDefaultMargins = 16843641; // 0x1010379 + field public static final int useDefaultMargins = 16843640; // 0x1010378 field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 field public static final int useLevel = 16843167; // 0x101019f field public static final int userVisible = 16843409; // 0x1010291 @@ -1046,10 +1045,10 @@ package android { field public static final int verticalCorrection = 16843322; // 0x101023a field public static final int verticalDivider = 16843054; // 0x101012e field public static final int verticalGap = 16843328; // 0x1010240 - field public static final int verticalOffset = 16843668; // 0x1010394 + field public static final int verticalOffset = 16843667; // 0x1010393 field public static final int verticalScrollbarPosition = 16843572; // 0x1010334 field public static final int verticalSpacing = 16843029; // 0x1010115 - field public static final int vibrationDuration = 16843665; // 0x1010391 + field public static final int vibrationDuration = 16843664; // 0x1010390 field public static final int visibility = 16842972; // 0x10100dc field public static final int visible = 16843156; // 0x1010194 field public static final int vmSafeMode = 16843448; // 0x10102b8 @@ -1066,7 +1065,7 @@ package android { field public static final int wallpaperIntraOpenExitAnimation = 16843416; // 0x1010298 field public static final int wallpaperOpenEnterAnimation = 16843411; // 0x1010293 field public static final int wallpaperOpenExitAnimation = 16843412; // 0x1010294 - field public static final int waveDrawable = 16843662; // 0x101038e + field public static final int waveDrawable = 16843661; // 0x101038d field public static final int webTextViewStyle = 16843449; // 0x10102b9 field public static final int webViewStyle = 16842885; // 0x1010085 field public static final int weekDayTextAppearance = 16843592; // 0x1010348 @@ -3737,7 +3736,11 @@ package android.app.backup { method public abstract void onBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor) throws java.io.IOException; method public void onCreate(); method public void onDestroy(); + method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException; method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException; + method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException; + field public static final int TYPE_DIRECTORY = 2; // 0x2 + field public static final int TYPE_FILE = 1; // 0x1 } public class BackupAgentHelper extends android.app.backup.BackupAgent { @@ -3789,6 +3792,9 @@ package android.app.backup { method public void writeNewStateDescription(android.os.ParcelFileDescriptor); } + public class FullBackupDataOutput { + } + public abstract class RestoreObserver { ctor public RestoreObserver(); method public void onUpdate(int, java.lang.String); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1ec7a96..eee14fb 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2013,15 +2013,10 @@ public final class ActivityThread { BackupAgent agent = null; String classname = data.appInfo.backupAgentName; - if (data.backupMode == IApplicationThread.BACKUP_MODE_FULL - || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL) { + // full backup operation but no app-supplied agent? use the default implementation + if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL + || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) { classname = "android.app.backup.FullBackupAgent"; - if ((data.appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - // system packages can supply their own full-backup agent - if (data.appInfo.fullBackupAgentName != null) { - classname = data.appInfo.fullBackupAgentName; - } - } } try { diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl index 8af78fa..087f83c 100644 --- a/core/java/android/app/IBackupAgent.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -51,7 +51,6 @@ oneway interface IBackupAgent { void doBackup(in ParcelFileDescriptor oldState, in ParcelFileDescriptor data, in ParcelFileDescriptor newState, - boolean storeApk, int token, IBackupManager callbackBinder); /** @@ -81,6 +80,25 @@ oneway interface IBackupAgent { in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder); /** + * Perform a "full" backup to the given file descriptor. The output file is presumed + * to be a socket or other non-seekable, write-only data sink. When this method is + * called, the app should write all of its files to the output. + * + * @param data Write-only file to receive the backed-up file content stream. + * The data must be formatted correctly for the resulting archive to be + * legitimate, so that will be tightly controlled by the available API. + * + * @param token Opaque token identifying this transaction. This must + * be echoed back to the backup service binder once the agent is + * finished restoring the application based on the restore data + * contents. + * + * @param callbackBinder Binder on which to indicate operation completion, + * passed here as a convenience to the agent. + */ + void doFullBackup(in ParcelFileDescriptor data, int token, IBackupManager callbackBinder); + + /** * Restore a single "file" to the application. The file was typically obtained from * a full-backup dataset. The agent reads 'size' bytes of file content * from the provided file descriptor. diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 63f3258..65c73f9 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -20,13 +20,22 @@ import android.app.IBackupAgent; import android.app.backup.IBackupManager; import android.content.Context; import android.content.ContextWrapper; +import android.content.pm.ApplicationInfo; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import java.io.File; import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; + +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.OsConstants; +import libcore.io.StructStat; /** * Provides the central interface between an @@ -87,6 +96,24 @@ public abstract class BackupAgent extends ContextWrapper { private static final String TAG = "BackupAgent"; private static final boolean DEBUG = true; + /** @hide */ + public static final int TYPE_EOF = 0; + + /** + * During a full restore, indicates that the file system object being restored + * is an ordinary file. + */ + public static final int TYPE_FILE = 1; + + /** + * During a full restore, indicates that the file system object being restored + * is a directory. + */ + public static final int TYPE_DIRECTORY = 2; + + /** @hide */ + public static final int TYPE_SYMLINK = 3; + public BackupAgent() { super(null); } @@ -179,18 +206,240 @@ public abstract class BackupAgent extends ContextWrapper { throws IOException; /** + * The default implementation backs up the entirety of the application's "owned" + * file system trees to the output. + */ + public void onFullBackup(FullBackupDataOutput data) throws IOException { + ApplicationInfo appInfo = getApplicationInfo(); + + String rootDir = new File(appInfo.dataDir).getAbsolutePath(); + String filesDir = getFilesDir().getAbsolutePath(); + String databaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath(); + String sharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath(); + String cacheDir = getCacheDir().getAbsolutePath(); + String libDir = (appInfo.nativeLibraryDir != null) + ? new File(appInfo.nativeLibraryDir).getAbsolutePath() + : null; + + // 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 (libDir != null) { + filterSet.add(libDir); + } + filterSet.add(cacheDir); + filterSet.add(databaseDir); + filterSet.add(sharedPrefsDir); + filterSet.add(filesDir); + fullBackupFileTree(packageName, FullBackup.ROOT_TREE_TOKEN, rootDir, filterSet, data); + + // Now do the same for the files dir, db dir, and shared prefs dir + filterSet.add(rootDir); + filterSet.remove(filesDir); + fullBackupFileTree(packageName, FullBackup.DATA_TREE_TOKEN, filesDir, filterSet, data); + + filterSet.add(filesDir); + filterSet.remove(databaseDir); + fullBackupFileTree(packageName, FullBackup.DATABASE_TREE_TOKEN, databaseDir, filterSet, data); + + filterSet.add(databaseDir); + filterSet.remove(sharedPrefsDir); + fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data); + } + + /** + * Write an entire file as part of a full-backup operation. The file's contents + * will be delivered to the backup destination along with the metadata necessary + * to place it with the proper location and permissions on the device where the + * data is restored. * @hide + * + * @param context The BackupAgent that is calling this method. It is an error to + * call it from something other than a running BackupAgent instance. + * @param file The file to be backed up. The file must exist and be readable by + * the caller. + * @param output The destination to which the backed-up file data will be sent. + */ + public final void fullBackupFile(File file, FullBackupDataOutput output) { + // Look up where all of our various well-defined dir trees live on this device + String mainDir; + String filesDir; + String dbDir; + String spDir; + String cacheDir; + String libDir; + + ApplicationInfo appInfo = getApplicationInfo(); + + mainDir = new File(appInfo.dataDir).getAbsolutePath(); + filesDir = getFilesDir().getAbsolutePath(); + dbDir = getDatabasePath("foo").getParentFile().getAbsolutePath(); + spDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath(); + cacheDir = getCacheDir().getAbsolutePath(); + libDir = (appInfo.nativeLibraryDir == null) ? null + : new File(appInfo.nativeLibraryDir).getAbsolutePath(); + + // Now figure out which well-defined tree the file is placed in, working from + // most to least specific. We also specifically exclude the lib and cache dirs. + String filePath = file.getAbsolutePath(); + + if (filePath.startsWith(cacheDir) || filePath.startsWith(libDir)) { + Log.w(TAG, "lib and cache files are not backed up"); + return; + } + + final String domain; + String rootpath = null; + if (filePath.startsWith(dbDir)) { + domain = FullBackup.DATABASE_TREE_TOKEN; + rootpath = dbDir; + } else if (filePath.startsWith(spDir)) { + domain = FullBackup.SHAREDPREFS_TREE_TOKEN; + rootpath = spDir; + } else if (filePath.startsWith(filesDir)) { + domain = FullBackup.DATA_TREE_TOKEN; + rootpath = filesDir; + } else if (filePath.startsWith(mainDir)) { + domain = FullBackup.ROOT_TREE_TOKEN; + rootpath = mainDir; + } else { + Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping"); + return; + } + + // And now that we know where it lives, semantically, back it up appropriately + Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain + + " rootpath=" + rootpath); + FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, + output.getData()); + } + + /** + * 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. + * + * @hide + */ + protected final void fullBackupFileTree(String packageName, String domain, String rootPath, + HashSet<String> excludes, FullBackupDataOutput output) { + File rootFile = new File(rootPath); + if (rootFile.exists()) { + LinkedList<File> scanQueue = new LinkedList<File>(); + scanQueue.add(rootFile); + + while (scanQueue.size() > 0) { + File file = scanQueue.remove(0); + String filePath = file.getAbsolutePath(); + + // prune this subtree? + if (excludes != null && excludes.contains(filePath)) { + continue; + } + + // If it's a directory, enqueue its contents for scanning. + try { + StructStat stat = Libcore.os.lstat(filePath); + if (OsConstants.S_ISLNK(stat.st_mode)) { + if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); + continue; + } else if (OsConstants.S_ISDIR(stat.st_mode)) { + File[] contents = file.listFiles(); + if (contents != null) { + for (File entry : contents) { + scanQueue.add(0, entry); + } + } + } + } catch (ErrnoException e) { + if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e); + continue; + } + + // Finally, back this file up before proceeding + FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, + output.getData()); + } + } + } + + /** + * Handle the data delivered via the given file descriptor during a full restore + * operation. The agent is given the path to the file's original location as well + * as its size and metadata. + * <p> + * The file descriptor can only be read for {@code size} bytes; attempting to read + * more data has undefined behavior. + * <p> + * The default implementation creates the destination file/directory and populates it + * with the data from the file descriptor, then sets the file's access mode and + * modification time to match the restore arguments. + * + * @param data A read-only file descriptor from which the agent can read {@code size} + * bytes of file data. + * @param size The number of bytes of file content to be restored to the given + * destination. If the file system object being restored is a directory, {@code size} + * will be zero. + * @param destination The File on disk to be restored with the given data. + * @param type The kind of file system object being restored. This will be either + * {@link BackupAgent#TYPE_FILE} or {@link BackupAgent#TYPE_DIRECTORY}. + * @param mode The access mode to be assigned to the destination after its data is + * written. This is in the standard format used by {@code chmod()}. + * @param mtime The modification time of the file when it was backed up, suitable to + * be assigned to the file after its data is written. + * @throws IOException */ public void onRestoreFile(ParcelFileDescriptor data, long size, - int type, String domain, String path, long mode, long mtime) + File destination, int type, long mode, long mtime) throws IOException { - // empty stub implementation + FullBackup.restoreFile(data, size, type, mode, mtime, destination); } /** - * Package-private, used only for dispatching an extra step during full backup + * Only specialized platform agents should overload this entry point to support + * restores to crazy non-app locations. + * @hide */ - void onSaveApk(BackupDataOutput data) { + protected void onRestoreFile(ParcelFileDescriptor data, long size, + int type, String domain, String path, long mode, long mtime) + throws IOException { + String basePath = null; + + if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type + + " domain=" + domain + " relpath=" + path + " mode=" + mode + + " mtime=" + mtime); + + // Parse out the semantic domains into the correct physical location + if (domain.equals(FullBackup.DATA_TREE_TOKEN)) { + basePath = getFilesDir().getAbsolutePath(); + } else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) { + basePath = getDatabasePath("foo").getParentFile().getAbsolutePath(); + } else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) { + basePath = new File(getApplicationInfo().dataDir).getAbsolutePath(); + } else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) { + basePath = getSharedPrefsFile("foo").getParentFile().getAbsolutePath(); + } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) { + basePath = getCacheDir().getAbsolutePath(); + } else { + // Not a supported location + Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring"); + } + + // Now that we've figured out where the data goes, send it on its way + if (basePath != null) { + File outFile = new File(basePath, path); + if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outFile.getPath()); + onRestoreFile(data, size, outFile, type, mode, mtime); + } else { + // Not a supported output location? We need to consume the data + // anyway, so just use the default "copy the data out" implementation + // with a null destination. + if (DEBUG) Log.i(TAG, "[ skipping data from unsupported domain " + domain + "]"); + FullBackup.restoreFile(data, size, type, mode, mtime, null); + } } // ----- Core implementation ----- @@ -215,7 +464,6 @@ public abstract class BackupAgent extends ContextWrapper { public void doBackup(ParcelFileDescriptor oldState, ParcelFileDescriptor data, ParcelFileDescriptor newState, - boolean storeApk, int token, IBackupManager callbackBinder) throws RemoteException { // Ensure that we're running with the app's normal permission level long ident = Binder.clearCallingIdentity(); @@ -223,10 +471,6 @@ public abstract class BackupAgent extends ContextWrapper { if (DEBUG) Log.v(TAG, "doBackup() invoked"); BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); - if (storeApk) { - onSaveApk(output); - } - try { BackupAgent.this.onBackup(oldState, output, newState); } catch (IOException ex) { @@ -273,6 +517,33 @@ public abstract class BackupAgent extends ContextWrapper { } @Override + public void doFullBackup(ParcelFileDescriptor data, + int token, IBackupManager callbackBinder) { + // Ensure that we're running with the app's normal permission level + long ident = Binder.clearCallingIdentity(); + + if (DEBUG) Log.v(TAG, "doFullBackup() invoked"); + BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); + + try { + BackupAgent.this.onFullBackup(new FullBackupDataOutput(data)); + } catch (IOException ex) { + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); + } catch (RuntimeException ex) { + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw ex; + } finally { + Binder.restoreCallingIdentity(ident); + try { + callbackBinder.opComplete(token); + } catch (RemoteException e) { + // we'll time out anyway, so we're safe + } + } + } + + @Override public void doRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime, int token, IBackupManager callbackBinder) throws RemoteException { diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 3b70e19..d7f1c9f 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -16,6 +16,9 @@ package android.app.backup; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -29,7 +32,8 @@ import libcore.io.Libcore; /** * Global constant definitions et cetera related to the full-backup-to-fd - * binary format. + * binary format. Nothing in this namespace is part of any API; it's all + * hidden details of the current implementation gathered into one location. * * @hide */ @@ -52,18 +56,41 @@ public class FullBackup { public static final String FULL_RESTORE_INTENT_ACTION = "fullrest"; public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken"; - public static final int TYPE_EOF = 0; - public static final int TYPE_FILE = 1; - public static final int TYPE_DIRECTORY = 2; - public static final int TYPE_SYMLINK = 3; - + /** + * @hide + */ static public native int backupToTar(String packageName, String domain, String linkdomain, String rootpath, String path, BackupDataOutput output); - static public void restoreToFile(ParcelFileDescriptor data, - long size, int type, long mode, long mtime, File outFile, - boolean doChmod) throws IOException { - if (type == FullBackup.TYPE_DIRECTORY) { + /** + * Copy data from a socket to the given File location on permanent storage. The + * modification time and access mode of the resulting file will be set if desired. + * If the {@code type} parameter indicates that the result should be a directory, + * the socket parameter may be {@code null}; even if it is valid, no data will be + * read from it in this case. + * <p> + * If the {@code mode} argument is negative, then the resulting output file will not + * have its access mode or last modification time reset as part of this operation. + * + * @param data Socket supplying the data to be copied to the output file. If the + * output is a directory, this may be {@code null}. + * @param size Number of bytes of data to copy from the socket to the file. At least + * this much data must be available through the {@code data} parameter. + * @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data + * or {@link BackupAgent#TYPE_DIRECTORY} for a directory. + * @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on + * the output file or directory. If this parameter is negative then neither + * the mode nor the mtime parameters will be used. + * @param mtime A timestamp in the standard Unix epoch that will be imposed as the + * 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. + * @throws IOException + */ + static public void restoreFile(ParcelFileDescriptor data, + long size, int type, long mode, long mtime, File outFile) throws IOException { + if (type == BackupAgent.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 // drop down to the final metadata adjustment. @@ -117,7 +144,7 @@ public class FullBackup { } // Now twiddle the state to match the backup, assuming all went well - if (doChmod && outFile != null) { + if (mode >= 0 && outFile != null) { try { Libcore.os.chmod(outFile.getPath(), (int)mode); } catch (ErrnoException e) { diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java index df1c363..faea76a 100644 --- a/core/java/android/app/backup/FullBackupAgent.java +++ b/core/java/android/app/backup/FullBackupAgent.java @@ -16,210 +16,26 @@ package android.app.backup; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Environment; import android.os.ParcelFileDescriptor; -import android.util.Log; - -import libcore.io.Libcore; -import libcore.io.ErrnoException; -import libcore.io.OsConstants; -import libcore.io.StructStat; - -import java.io.File; import java.io.IOException; -import java.util.HashSet; -import java.util.LinkedList; /** - * Backs up an application's entire /data/data/<package>/... file system. This - * class is used by the desktop full backup mechanism and is not intended for direct - * use by applications. + * Simple concrete class that merely provides the default BackupAgent full backup/restore + * implementations for applications that do not supply their own. * * {@hide} */ public class FullBackupAgent extends BackupAgent { - // !!! TODO: turn off debugging - private static final String TAG = "FullBackupAgent"; - private static final boolean DEBUG = true; - - PackageManager mPm; - - private String mMainDir; - private String mFilesDir; - private String mDatabaseDir; - private String mSharedPrefsDir; - private String mCacheDir; - private String mLibDir; - - private File NULL_FILE; - - @Override - public void onCreate() { - NULL_FILE = new File("/dev/null"); - - mPm = getPackageManager(); - try { - ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0); - mMainDir = new File(appInfo.dataDir).getAbsolutePath(); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Unable to find package " + getPackageName()); - throw new RuntimeException(e); - } - - mFilesDir = getFilesDir().getAbsolutePath(); - mDatabaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath(); - mSharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath(); - mCacheDir = getCacheDir().getAbsolutePath(); - - ApplicationInfo app = getApplicationInfo(); - mLibDir = (app.nativeLibraryDir != null) - ? new File(app.nativeLibraryDir).getAbsolutePath() - : null; - } - @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 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) { - filterSet.add(mLibDir); - } - filterSet.add(mCacheDir); - filterSet.add(mDatabaseDir); - filterSet.add(mSharedPrefsDir); - filterSet.add(mFilesDir); - 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(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data); - - filterSet.add(mFilesDir); - filterSet.remove(mDatabaseDir); - processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data); - - filterSet.add(mDatabaseDir); - filterSet.remove(mSharedPrefsDir); - processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data); + // Doesn't do incremental backup/restore } - // 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) { - File rootFile = new File(rootPath); - if (rootFile.exists()) { - LinkedList<File> scanQueue = new LinkedList<File>(); - scanQueue.add(rootFile); - - while (scanQueue.size() > 0) { - File file = scanQueue.remove(0); - String filePath = file.getAbsolutePath(); - - // prune this subtree? - if (excludes != null && excludes.contains(filePath)) { - continue; - } - - // If it's a directory, enqueue its contents for scanning. - try { - StructStat stat = Libcore.os.lstat(filePath); - if (OsConstants.S_ISLNK(stat.st_mode)) { - if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); - continue; - } else if (OsConstants.S_ISDIR(stat.st_mode)) { - File[] contents = file.listFiles(); - if (contents != null) { - for (File entry : contents) { - scanQueue.add(0, entry); - } - } - } - } catch (ErrnoException e) { - if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e); - continue; - } - - // Finally, back this file up before proceeding - FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data); - } - } - } - - @Override - void onSaveApk(BackupDataOutput data) { - ApplicationInfo app = getApplicationInfo(); - if (DEBUG) Log.i(TAG, "APK flags: system=" + ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) - + " updated=" + ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) - + " locked=" + ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) ); - if (DEBUG) Log.i(TAG, "codepath: " + getPackageCodePath()); - - // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here - final String pkgName = getPackageName(); - final String apkDir = new File(getPackageCodePath()).getParent(); - FullBackup.backupToTar(pkgName, FullBackup.APK_TREE_TOKEN, null, - apkDir, getPackageCodePath(), data); - - // Save associated .obb content if it exists and we did save the apk - // check for .obb and save those too - final File obbDir = Environment.getExternalStorageAppObbDirectory(pkgName); - if (obbDir != null) { - if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); - File[] obbFiles = obbDir.listFiles(); - if (obbFiles != null) { - final String obbDirName = obbDir.getAbsolutePath(); - for (File obb : obbFiles) { - FullBackup.backupToTar(pkgName, FullBackup.OBB_TREE_TOKEN, null, - obbDirName, obb.getAbsolutePath(), data); - } - } - } - } - - /** - * Dummy -- We're never used for restore of an incremental dataset - */ @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { - } - - /** - * Restore the described file from the given pipe. - */ - @Override - public void onRestoreFile(ParcelFileDescriptor data, long size, - int type, String domain, String relpath, long mode, long mtime) - throws IOException { - String basePath = null; - File outFile = null; - - if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type - + " domain=" + domain + " relpath=" + relpath + " mode=" + mode - + " mtime=" + mtime); - - // Parse out the semantic domains into the correct physical location - if (domain.equals(FullBackup.DATA_TREE_TOKEN)) basePath = mFilesDir; - else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) basePath = mDatabaseDir; - else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) basePath = mMainDir; - else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) basePath = mSharedPrefsDir; - - // Not a supported output location? We need to consume the data - // anyway, so send it to /dev/null - outFile = (basePath != null) ? new File(basePath, relpath) : null; - 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, true); + // Doesn't do incremental backup/restore } } diff --git a/core/java/android/app/backup/FullBackupDataOutput.java b/core/java/android/app/backup/FullBackupDataOutput.java new file mode 100644 index 0000000..99dab1f --- /dev/null +++ b/core/java/android/app/backup/FullBackupDataOutput.java @@ -0,0 +1,21 @@ +package android.app.backup; + +import android.os.ParcelFileDescriptor; + +/** + * Provides the interface through which a {@link BackupAgent} writes entire files + * to a full backup data set, via its {@link BackupAgent#onFullBackup(FullBackupDataOutput)} + * method. + */ +public class FullBackupDataOutput { + // Currently a name-scoping shim around BackupDataOutput + private BackupDataOutput mData; + + /** @hide */ + public FullBackupDataOutput(ParcelFileDescriptor fd) { + mData = new BackupDataOutput(fd.getFileDescriptor()); + } + + /** @hide */ + public BackupDataOutput getData() { return mData; } +} diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 454cb31..ddb6ef0 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -91,15 +91,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String backupAgentName; /** - * Class implementing the package's *full* backup functionality. This - * is not usable except by system-installed packages. It can be the same - * as the backupAgent. - * - * @hide - */ - public String fullBackupAgentName; - - /** * Value for {@link #flags}: if set, this application is installed in the * device's system image. */ @@ -555,7 +546,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(installLocation); dest.writeString(manageSpaceActivityName); dest.writeString(backupAgentName); - dest.writeString(fullBackupAgentName); dest.writeInt(descriptionRes); } @@ -593,7 +583,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { installLocation = source.readInt(); manageSpaceActivityName = source.readString(); backupAgentName = source.readString(); - fullBackupAgentName = source.readString(); descriptionRes = source.readInt(); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 208869b..53d6bb1 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1517,17 +1517,6 @@ 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 (false) { - Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName - + " from " + pkgName + "+" + name); - } - } - TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestApplication_label); if (v != null && (ai.labelRes=v.resourceId) == 0) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1a32060..49eaf19 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1418,7 +1418,6 @@ android:label="@string/android_system_label" android:allowClearUserData="false" android:backupAgent="com.android.server.SystemBackupAgent" - android:fullBackupAgent="com.android.server.SystemBackupAgent" android:killAfterRestore="false" android:icon="@drawable/ic_launcher_android"> <activity android:name="com.android.internal.app.ChooserActivity" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 03b332e..dd16bd0 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -696,13 +696,6 @@ <p>The default value of this attribute is <code>false</code>. --> <attr name="restoreAnyVersion" format="boolean" /> - <!-- The agent to use for a *full* backup of the package. Only system applications - can use this to override the ordinary FullBackupAgent with a custom implementation. - It's needed strictly for packages with strongly device-specific data, such as the - Settings provider. - --> - <attr name="fullBackupAgent" format="string" /> - <!-- The default install location defined by an application. --> <attr name="installLocation"> <!-- Let the system decide ideal install location --> @@ -800,7 +793,6 @@ <attr name="killAfterRestore" /> <attr name="restoreNeedsApplication" /> <attr name="restoreAnyVersion" /> - <attr name="fullBackupAgent" /> <attr name="neverEncrypt" /> <!-- Request that your application's processes be created with a large Dalvik heap. This applies to <em>all</em> processes diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 54e484e..20f5e7c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1725,7 +1725,6 @@ <public type="attr" name="layoutDirection" /> - <public type="attr" name="fullBackupAgent" /> <public type="attr" name="suggestionsEnabled" /> <public type="attr" name="rowCount" /> @@ -1737,7 +1736,6 @@ <public type="attr" name="layout_row" /> <public type="attr" name="layout_rowSpan" /> - <public type="attr" name="layout_columnSpan" /> <public type="attr" name="layout_widthSpec" /> diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index e5f52e2..dd0d064 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -6,7 +6,6 @@ android:label="@string/app_label" android:process="system" android:backupAgent="SettingsBackupAgent" - android:fullBackupAgent="SettingsBackupAgent" android:killAfterRestore="false" android:icon="@drawable/ic_launcher_settings"> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 9469601..3a7a6e1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -36,7 +36,7 @@ import java.util.zip.CRC32; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupAgentHelper; -import android.app.backup.FullBackup; +import android.app.backup.FullBackupDataOutput; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -132,58 +132,22 @@ public class SettingsBackupAgent extends BackupAgentHelper { byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT); byte[] wifiConfigData = getFileData(mWifiConfigFile); - // This same agent class is used for both full and incremental backups. A full - // backup is flagged by a 'null' oldState argument. In the case of a full backup, - // the output is structured as tarfile contents. - if (oldState != null) { - long[] stateChecksums = readOldChecksums(oldState); - - stateChecksums[STATE_SYSTEM] = - writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data); - stateChecksums[STATE_SECURE] = - writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data); - stateChecksums[STATE_LOCALE] = - writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data); - stateChecksums[STATE_WIFI_SUPPLICANT] = - writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT, - wifiSupplicantData, data); - stateChecksums[STATE_WIFI_CONFIG] = - writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData, - data); - - writeNewChecksums(stateChecksums, newState); - } else { - // Write the data to the staging file, then emit that as our tarfile - // representation of the backed-up settings. - String root = getFilesDir().getAbsolutePath(); - File stage = new File(root, STAGE_FILE); - try { - FileOutputStream filestream = new FileOutputStream(stage); - BufferedOutputStream bufstream = new BufferedOutputStream(filestream); - DataOutputStream out = new DataOutputStream(bufstream); - - out.writeInt(FULL_BACKUP_VERSION); - - out.writeInt(systemSettingsData.length); - out.write(systemSettingsData); - out.writeInt(secureSettingsData.length); - out.write(secureSettingsData); - out.writeInt(locale.length); - out.write(locale); - out.writeInt(wifiSupplicantData.length); - out.write(wifiSupplicantData); - out.writeInt(wifiConfigData.length); - out.write(wifiConfigData); - - out.flush(); // also flushes downstream - - // now we're set to emit the tar stream - FullBackup.backupToTar(getPackageName(), FullBackup.DATA_TREE_TOKEN, null, - root, stage.getAbsolutePath(), data); - } finally { - stage.delete(); - } - } + long[] stateChecksums = readOldChecksums(oldState); + + stateChecksums[STATE_SYSTEM] = + writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data); + stateChecksums[STATE_SECURE] = + writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data); + stateChecksums[STATE_LOCALE] = + writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data); + stateChecksums[STATE_WIFI_SUPPLICANT] = + writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT, + wifiSupplicantData, data); + stateChecksums[STATE_WIFI_CONFIG] = + writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData, + data); + + writeNewChecksums(stateChecksums, newState); } @Override @@ -221,6 +185,45 @@ public class SettingsBackupAgent extends BackupAgentHelper { } @Override + public void onFullBackup(FullBackupDataOutput data) throws IOException { + byte[] systemSettingsData = getSystemSettings(); + byte[] secureSettingsData = getSecureSettings(); + byte[] locale = mSettingsHelper.getLocaleData(); + byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT); + byte[] wifiConfigData = getFileData(mWifiConfigFile); + + // Write the data to the staging file, then emit that as our tarfile + // representation of the backed-up settings. + String root = getFilesDir().getAbsolutePath(); + File stage = new File(root, STAGE_FILE); + try { + FileOutputStream filestream = new FileOutputStream(stage); + BufferedOutputStream bufstream = new BufferedOutputStream(filestream); + DataOutputStream out = new DataOutputStream(bufstream); + + out.writeInt(FULL_BACKUP_VERSION); + + out.writeInt(systemSettingsData.length); + out.write(systemSettingsData); + out.writeInt(secureSettingsData.length); + out.write(secureSettingsData); + out.writeInt(locale.length); + out.write(locale); + out.writeInt(wifiSupplicantData.length); + out.write(wifiSupplicantData); + out.writeInt(wifiConfigData.length); + out.write(wifiConfigData); + + out.flush(); // also flushes downstream + + // now we're set to emit the tar stream + fullBackupFile(stage, data); + } finally { + stage.delete(); + } + } + + @Override public void onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String relpath, long mode, long mtime) throws IOException { diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml index 258059c..39c36f1 100644 --- a/packages/SharedStorageBackup/AndroidManifest.xml +++ b/packages/SharedStorageBackup/AndroidManifest.xml @@ -23,7 +23,7 @@ <application android:allowClearUserData="false" android:permission="android.permission.CONFIRM_FULL_BACKUP" - android:fullBackupAgent=".SharedStorageAgent" + android:backupAgent=".SharedStorageAgent" android:allowBackup="false" > </application> </manifest> diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java index b02ca2e..6c677b8 100644 --- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java +++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java @@ -1,9 +1,10 @@ package com.android.sharedstoragebackup; -import android.app.backup.FullBackup; import android.app.backup.FullBackupAgent; +import android.app.backup.FullBackup; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.FullBackupDataOutput; import android.content.Context; import android.os.Environment; import android.os.ParcelFileDescriptor; @@ -30,9 +31,11 @@ public class SharedStorageAgent extends FullBackupAgent { } } + /** + * Full backup of the shared-storage filesystem + */ @Override - public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) throws IOException { + public void onFullBackup(FullBackupDataOutput output) 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. @@ -43,20 +46,12 @@ public class SharedStorageAgent extends FullBackupAgent { // 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); + fullBackupFileTree(null, domain, v.getPath(), null, output); } } } /** - * 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 @@ -88,6 +83,6 @@ public class SharedStorageAgent extends FullBackupAgent { Slog.e(TAG, "Skipping data with malformed path " + relpath); } - FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false); + FullBackup.restoreFile(data, size, type, -1, mtime, outFile); } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 3aa1239..6afccec 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -23,6 +23,7 @@ import android.app.IActivityManager; import android.app.IApplicationThread; import android.app.IBackupAgent; import android.app.PendingIntent; +import android.app.backup.BackupAgent; import android.app.backup.BackupDataOutput; import android.app.backup.FullBackup; import android.app.backup.RestoreSet; @@ -64,6 +65,7 @@ import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings; import android.util.EventLog; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -1587,8 +1589,7 @@ class BackupManagerService extends IBackupManager.Stub { // Initiate the target's backup pass prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL); - agent.doBackup(savedState, backupData, newState, false, - token, mBackupManagerBinder); + agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder); boolean success = waitUntilOperationComplete(token); if (!success) { @@ -1764,30 +1765,31 @@ class BackupManagerService extends IBackupManager.Stub { if (agent != null) { try { ApplicationInfo app = pkg.applicationInfo; - boolean sendApk = mIncludeApks + final 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()); + 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, "Writing manifest for " + pkg.packageName); + writeAppManifest(pkg, mManifestFile, sendApk); + FullBackup.backupToTar(pkg.packageName, null, null, + mFilesDir.getAbsolutePath(), + mManifestFile.getAbsolutePath(), + output); + + if (sendApk) { + writeApkToBackup(pkg, output); } - if (DEBUG) Slog.d(TAG, "Calling doBackup()"); + if (DEBUG) Slog.d(TAG, "Calling doFullBackup()"); final int token = generateToken(); prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL); - agent.doBackup(null, mOutputFile, null, sendApk, - token, mBackupManagerBinder); + agent.doFullBackup(mOutputFile, token, mBackupManagerBinder); if (!waitUntilOperationComplete(token)) { Slog.e(TAG, "Full backup failed on package " + pkg.packageName); } else { @@ -1802,6 +1804,29 @@ class BackupManagerService extends IBackupManager.Stub { tearDown(pkg); } + private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) { + // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here + final String appSourceDir = pkg.applicationInfo.sourceDir; + final String apkDir = new File(appSourceDir).getParent(); + FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null, + apkDir, appSourceDir, output); + + // Save associated .obb content if it exists and we did save the apk + // check for .obb and save those too + final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName); + if (obbDir != null) { + if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); + File[] obbFiles = obbDir.listFiles(); + if (obbFiles != null) { + final String obbDirName = obbDir.getAbsolutePath(); + for (File obb : obbFiles) { + FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null, + obbDirName, obb.getAbsolutePath(), output); + } + } + } + } + private void backupSharedStorage() throws RemoteException { PackageInfo pkg = null; try { @@ -1813,7 +1838,7 @@ class BackupManagerService extends IBackupManager.Stub { final int token = generateToken(); prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL); - agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder); + agent.doFullBackup(mOutputFile, token, mBackupManagerBinder); if (!waitUntilOperationComplete(token)) { Slog.e(TAG, "Full backup failed on shared storage"); } else { @@ -1933,7 +1958,7 @@ class BackupManagerService extends IBackupManager.Stub { static class FileMetadata { String packageName; // name of the owning app String installerPackageName; // name of the market-type app that installed the owner - int type; // e.g. FullBackup.TYPE_DIRECTORY + int type; // e.g. BackupAgent.TYPE_DIRECTORY String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN String path; // subpath within the semantic domain long mode; // e.g. 0666 (actually int) @@ -2182,15 +2207,15 @@ class BackupManagerService extends IBackupManager.Stub { // If we haven't sent any data to this app yet, we probably // need to clear it first. Check that. if (!mClearedPackages.contains(pkg)) { - // apps with their own full backup agents are + // apps with their own backup agents are // responsible for coherently managing a full // restore. - if (mTargetApp.fullBackupAgentName == null) { + if (mTargetApp.backupAgentName == null) { if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); clearApplicationDataSynchronous(pkg); } else { - if (DEBUG) Slog.d(TAG, "full backup agent (" - + mTargetApp.fullBackupAgentName + ") => no clear"); + if (DEBUG) Slog.d(TAG, "backup agent (" + + mTargetApp.backupAgentName + ") => no clear"); } mClearedPackages.add(pkg); } else { @@ -2686,7 +2711,7 @@ class BackupManagerService extends IBackupManager.Stub { StringBuilder b = new StringBuilder(128); // mode string - b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-'); + 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' : '-'); @@ -2746,9 +2771,9 @@ class BackupManagerService extends IBackupManager.Stub { } switch (typeChar) { - case '0': info.type = FullBackup.TYPE_FILE; break; + case '0': info.type = BackupAgent.TYPE_FILE; break; case '5': { - info.type = FullBackup.TYPE_DIRECTORY; + info.type = BackupAgent.TYPE_DIRECTORY; if (info.size != 0) { Slog.w(TAG, "Directory entry with nonzero size in header"); info.size = 0; diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index 08c6699..950f3b6 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -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, true); + FullBackup.restoreFile(data, size, type, mode, mtime, outFile); if (restoredWallpaper) { WallpaperManagerService wallpaper = |