This commit makes a few changes towards establishing a formal application
interface for interacting with the backup/restore mechanism:
1. Introduce public wrapper classes around the various binder interfaces; 3rd
party code will never see the binders directly.
2. Progress update callbacks during a restore sequence now occur on the main
thread, not in a binder thread [and not with system-process permissions!].
3. Rename the BackupManagerService's inner "RestoreSession" class to avoid
ambiguity with the new public "RestoreSession" class.
We can now locate event log tag definitions in individual packages
(and java constants for the tag numbers get auto-generated), so move
all the tags used by the system server into the package.
The underlying issue is that ordinarily, the restore mechanism checks the
signature block of the package which uploaded the restore data against the
signature of the package on-device that will be handling the restore. This is
to ensure that the restore goes to the "same" application as the one that
uploaded it.
However, applications bundled on the system partition as part of the device
build are signed not with a permanent, app-identifying signature, but rather
with the device's signature block, which is different for each kind of device
and potentially between kinds of build for the same device. That renders the
stored backup data unusable when changing devices / builds / etc.
This change works around the problem by explicitly privileging applications that
reside on the system partition: they have been built into the device, and so
are trusted to be able to handle data that is marked as coming from its package
name.
Change-Id: I561011bce2b54cff3e695e82e7544b126b6ac31e
and add some random fuzz to the scheduling times, to make sure we aren't
creating hour-aligned server load spikes.
See bug 2226553 for details & Dr. No information.
to defensively work around a still-mysterious bug where the
list of saved packages ends up being empty even though we still
have state pending. If we do re-initialize, then wipe all state
to make sure the right thing happens.
Don't keep open journal files -- close them after every update.
A bit less efficient, but possibly more reliable (again, this is
defensive programming here). Also change "rwd" to "rws" mode
for fully synchronous operation.
on success, record "backup_initialize" event; on failure,
record "backup_transport_failure" event (and add tags to
"backup_transport_failure" events that aren't associated
with a particular package -- namely "(initialize)" and
"(finish)").
If a backup pass had been skipped (either because the transport was unavailable
or -- in a common case! -- because there was simply no work pending when the
periodic backup check fired), we were forgetting to reset the "backup currently
in progress" flag. Once we'd done that, the device would *NEVER* perform a
backup until it was rebooted, since it would forever think that there was one
currently in operation that must not be interfered with.
Change-Id: I0d6d7375dc6de99b599222a449934e70fe13ebb9
* We now check for in-progress backups when asked to perform one, and don't
bother firing off another one concurrently (nor do we adjust the scheduling;
after all, someone asked to do a backup "now" and we're doing one, so we are
in line with expectations). We also defer backup passes while a restore is
in flight, and abort attempts to perform a restore during a backup pass.
* When a backup attempt fails, we now ask the transport how far in the future we
should put our retry. Previously we were simply not bothering to consult with
the transport, so we would wind up retrying backup while network connectivity
was known to be down, etc.
Change-Id: Ic7758010b74e06e90c50d46b7b0dd01b17af7c90
Turning off backup in the Settings UI constitutes an opt-out of the whole
mechanism. For privacy reasons we instruct the backend to wipe all of the data
belonging to this device when the user does this. If the attempt fails it is
rescheduled in the future based on the transport's requestBackupTime()
suggestion. If network connectivity changes prompt the transport to indicate a
backup pass is appropriate "now," any pending init operation is processed before
the backup schedule is resumed.
The broadcasts used internally to the backup manager are now fully protected;
third party apps can neither send nor receive them.
(Also a minor logging change; don't log 'appropriate' EOF encountered during
parsing of a backup data stream.)
return TRANSPORT_NOT_INITIALIZED (in fact that's typically how it comes).
For consistency, make other IBackupTransport methods return int instead of
boolean, and handle accordingly.
Make initializeDevice() its own method instead of a flag on performBackup().
This will be needed when un-checking the settings box anyway, and is
conceptually unrelated to whatever happens to be the first post-initialization
backup we perform. (Note that even if the init is sent separately from the
backup operation, the server will remember that an init has been done and
will *not* return NOT_INITIALIZED for the subsequent backup.)
Fix LocalTransport accordingly (trivial changes).
Handle failures more robustly in BackupManagerService -- most notably,
doQueuedBackups() was ignoring the result code of processOneBackup(), so
a NOT_INITIALIZED error would go past unseen (at least until the next
backup pass). Keep track of error code returns more universally now.
(This includes finishBackup(), of course.)
When attempting a backup, the transport may inform us that the backend is in an
uninitialized state. This typically means that the device's data has been wiped
after a period [e.g. 90 days] of inactivity. This means that we need to
re-store all data subject to backup, and all of our incremental state tracking
on the device is now stale.
In response, we wipe all of our recorded backup state and restart the backup
pass on all participants.
This changes a couple of bits of the Backup Manager's logic around
first-backup-ever handling. First, it uses the same "what we've ever backed up"
handling as the rest of the system for purposes of instructing the backend to do
a wipe when the metadata is initialized for the device, instead of looking at
the presence of the @pm@ file. Secondly, for the time being we won't be using
the saved state produced by agents' doRestore() logic. Until the server is
migrating each restored package's data over to the new dataset, this was
resulting in the client holding an agent state blob for which no data existed on
the server under this device's identity but which the device thought was fully
up to date.
IBackupTransport.performBackup() now takes a flag "wipeAllFirst", which if set
will result in the entire restore set for the current device/account being wiped
clean prior to the storage of the provided package. This ensures that a device
on which backup has just been enabled will not confront potentially-stale
information, nor will the restore set potentially contain mismatched data from
orphaned packages.
The Backup Manager has also been revised to pass this flag when first backing up
its master metadata block (and never pass it thereafter unless something has
caused the backup state tracking to be erased, e.g. the user has opted out of
backup and then later re-enabled it).
Applications can now specify two more aspects of the restore process: whether
they need to run with their own custom Application subclass rather than being
launched in the usual restricted mode during restore, and whether it's okay for
the backup manager to kill the app process once restore has completed. The new
manifest attributes for these are, respectively, android:restoreNeedsApplication
and android:killAfterRestore.
If unspecified in the manifest, restoreNeedsApplication is false, and
killAfterRestore is true.
In order to support kill-after-restore cleanly, this change also adds a new
system-process-only interface to the Activity Manager, which will schedule a
"commit suicide" event on the target app's main thread looper.
The framework backup agents have been given the appropriate new backup
attributes as well.
Packages that do not use android.permission.BACKUP_DATA will neither be backed
up nor restored. That permission is currently signature-only. In the future if
access to the backup/restore infrastructure is made available to arbitrary 3rd
party applications, the permission checks (and indeed, the permission itself)
can simply be removed.
Specifically, don't wait for the RestoreObserver to be informed that the restore
has completed unless performRestore() ran. We were winding up in a case where
bmgr was hanging forever waiting on a nonexistent restore process instead of
calling endRestoreSession().
Also improve the documentation, explicitly calling out the need to call
endRestoreSession() even if previous operations on the session were
unsuccessful.
This is because doing it through the Activity Manager also makes sure that the
app has been shut down first, which is quite a desirable invariant for restore
handling.
When a restore agent throws an exception back into the framework, assume that it
may have left the app's data in an inconsistent state and clear the data for
that app (again -- it was cleared just prior to the attempted restore, too).
When a package is uninstalled we now remove our notion of its having been backed
up, thereby forcing a backup pass if it is reinstalled at some point in the
future. Removal from the log means rewriting it and doing an atomic rename to
the canonical version. The temporary existence used during the rewrite is *not*
written synchronously; there's no need and it keeps the load on the flash part
much lower.
Because we might crash & reboot in the middle of the rewrite operation, there's
now code during init that sanity-checks the contents of the ever-backed-up log
and ensures that it's in a coherent state.
We now schedule a backup pass for any new application we see (at boot or
package-install time) for which we have never performed a backup. The
bookkeeping is a log file with the names of all the packages we've successfully
completed a backup pass on, maintained with synchronous journal-type behavior.
Also, make a bunch of private fields package-scoped to permit cheaper access
from the various worker / binder threads that run under the Backup Manager's
aegis.
This also means we should no longer do explicit calling-identity fiddling in
dump(), relying instead on the usual permission enforcement mechanism to handle
things.
If there's no data to be backed up, we no longer invoke the transport. We *DO*
still require that the agent have emitted a valid new state file, however.
This change also finally uncomments the code that removes the backup data file
after it has been sent to the transport, so there will be no more
packagename.data files lying around in the staging area.
We now log in dataChanged() only when an app is added to the backup set (and
*not* if it redundantly asks to be added after it's already there) and on error.
We now only commit to the pending-backup journal on disk the first time that a
given package is added to the backup set. This avoids a lot of write thrashing
of the disk, particularly since Settings tends to call dataChanged() a great
many times during boot, while the Settings UI is in use, etc.
This CL adds the concept of 'provisioned' to the backup manager. No backups
will be scheduled until the user has indicated that backups are to be enabled
*and* has clicked all the way through the setup wizard.
When the user first turns on the backup system, the delay before the initial
backup pass is different from the periodic backup interval. Currently that
initial delay is 12 hours. The intent here is to guess at a less-active time
for performing that first backup pass.
NOTE: currently the backup service defaults to 'provisioned'. Once the real
code goes live in Setup Wizard, this will be changed to default to
not-provisioned until the user has confirmed all the relevant UI.
We need to make sure we stay alive for the duration of a backup or (especially)
restore operation. The existing Handler-based timing system was simply not
properly functional, so it's been retooled to use a repeating alarm delivering a
broastcast PendingIntent to our registered receiver.
We acquire a partial wake lock in the broadcast receiver [i.e. while the Alarm
Manager is holding one for the duration of broadcast delivery] and pass the
wakelock object to the backup thread, which eventually releases it when it's
finsihed operations. A similar pattern is used for the threads handling restore
and clear.
Without the metadata we can't verify the version number or the signatures of the
apps whose data we'd be trying to restore against the apps present on device.
This is not acceptable; we need to refuse to give data to an unauthenticated
app.
It's now possible to ask that the backup manager wipe the saved data for a given
application from the backing store. LocalTransport implements this now but the
Google backend does not yet. When the data is wiped, the on-device backup state
is also wiped to ensure that the next backup pushes all necessary data.
Bmgr has not yet been modified to actually call into this method, but it will
be soon.