am 4cc86e1a: Clear the device\'s data from the transport when backup is disabled

Merge commit '4cc86e1ae80eb1938500fe5fa06bbdf8b4b7b50d' into eclair-plus-aosp

* commit '4cc86e1ae80eb1938500fe5fa06bbdf8b4b7b50d':
  Clear the device's data from the transport when backup is disabled
This commit is contained in:
Christopher Tate
2009-09-24 11:36:07 -07:00
committed by Android Git Automerger
3 changed files with 243 additions and 20 deletions

View File

@@ -52,6 +52,10 @@
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.backup.intent.RUN" />
<protected-broadcast android:name="android.backup.intent.CLEAR" />
<protected-broadcast android:name="android.backup.intent.INIT" />
<protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />

View File

@@ -196,8 +196,9 @@ BackupDataReader::Status()
m_done = true; \
} else { \
m_status = errno; \
LOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \
long(actual), long(expected), __LINE__, strerror(m_status)); \
} \
LOGD("CHECK_SIZE failed with at line %d m_status='%s'", __LINE__, strerror(m_status)); \
return m_status; \
} \
} while(0)

View File

@@ -31,7 +31,6 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
@@ -67,6 +66,7 @@ import com.android.server.PackageManagerBackupAgent.Metadata;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
@@ -89,11 +89,14 @@ class BackupManagerService extends IBackupManager.Stub {
// the first backup pass.
private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
private static final String RUN_BACKUP_ACTION = "_backup_run_";
private static final String RUN_BACKUP_ACTION = "android.backup.intent.RUN";
private static final String RUN_INITIALIZE_ACTION = "android.backup.intent.INIT";
private static final String RUN_CLEAR_ACTION = "android.backup.intent.CLEAR";
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
private static final int MSG_RUN_RESTORE = 3;
private static final int MSG_RUN_CLEAR = 4;
private static final int MSG_RUN_INITIALIZE = 5;
// Event tags -- see system/core/logcat/event-log-tags
private static final int BACKUP_DATA_CHANGED_EVENT = 2820;
@@ -123,9 +126,8 @@ class BackupManagerService extends IBackupManager.Stub {
boolean mProvisioned;
PowerManager.WakeLock mWakelock;
final BackupHandler mBackupHandler = new BackupHandler();
PendingIntent mRunBackupIntent;
BroadcastReceiver mRunBackupReceiver;
IntentFilter mRunBackupFilter;
PendingIntent mRunBackupIntent, mRunInitIntent;
BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
// map UIDs to the set of backup client services within that UID's app set
final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
= new SparseArray<HashSet<ApplicationInfo>>();
@@ -206,6 +208,10 @@ class BackupManagerService extends IBackupManager.Stub {
private RandomAccessFile mEverStoredStream;
HashSet<String> mEverStoredApps = new HashSet<String>();
// Persistently track the need to do a full init
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
boolean mInitInProgress = false;
public BackupManagerService(Context context) {
mContext = context;
@@ -218,23 +224,32 @@ class BackupManagerService extends IBackupManager.Stub {
// Set up our bookkeeping
boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.BACKUP_ENABLED, 0) != 0;
// !!! TODO: mProvisioned needs to default to 0, not 1.
mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mDataDir = Environment.getDownloadCacheDirectory();
// Alarm receivers for scheduled backups & initialization operations
mRunBackupReceiver = new RunBackupReceiver();
mRunBackupFilter = new IntentFilter();
mRunBackupFilter.addAction(RUN_BACKUP_ACTION);
context.registerReceiver(mRunBackupReceiver, mRunBackupFilter);
IntentFilter filter = new IntentFilter();
filter.addAction(RUN_BACKUP_ACTION);
context.registerReceiver(mRunBackupReceiver, filter,
android.Manifest.permission.BACKUP, null);
mRunInitReceiver = new RunInitializeReceiver();
filter = new IntentFilter();
filter.addAction(RUN_INITIALIZE_ACTION);
context.registerReceiver(mRunInitReceiver, filter,
android.Manifest.permission.BACKUP, null);
Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
// !!! TODO: restrict delivery to our receiver; the naive setClass() doesn't seem to work
//backupIntent.setClass(context, mRunBackupReceiver.getClass());
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
// Set up the backup-request journaling
mJournalDir = new File(mBaseStateDir, "pending");
mJournalDir.mkdirs(); // creates mBaseStateDir along the way
@@ -287,14 +302,52 @@ class BackupManagerService extends IBackupManager.Stub {
private class RunBackupReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
if (DEBUG) Log.v(TAG, "Running a backup pass");
synchronized (mQueueLock) {
// acquire a wakelock and pass it to the backup thread. it will
// be released once backup concludes.
if (mPendingInits.size() > 0) {
// If there are pending init operations, we process those
// and then settle into the usual periodic backup schedule.
if (DEBUG) Log.v(TAG, "Init pending at scheduled backup");
try {
mAlarmManager.cancel(mRunInitIntent);
mRunInitIntent.send();
} catch (PendingIntent.CanceledException ce) {
Log.e(TAG, "Run init intent cancelled");
// can't really do more than bail here
}
} else {
// Don't run backups now if we're disabled, not yet
// fully set up, or racing with an initialize pass.
if (mEnabled && mProvisioned && !mInitInProgress) {
if (DEBUG) Log.v(TAG, "Running a backup pass");
// Acquire the wakelock and pass it to the backup thread. it will
// be released once backup concludes.
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
mBackupHandler.sendMessage(msg);
} else {
Log.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned
+ " i=" + mInitInProgress);
}
}
}
}
}
}
private class RunInitializeReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
synchronized (mQueueLock) {
if (DEBUG) Log.v(TAG, "Running a device init");
mInitInProgress = true;
// Acquire the wakelock and pass it to the init thread. it will
// be released once init concludes.
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
mBackupHandler.sendMessage(msg);
}
}
@@ -408,6 +461,37 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
// Maintain persistent state around whether need to do an initialize operation.
// Must be called with the queue lock held.
void recordInitPendingLocked(boolean isPending, String transportName) {
if (DEBUG) Log.i(TAG, "recordInitPendingLocked: " + isPending
+ " on transport " + transportName);
try {
IBackupTransport transport = getTransport(transportName);
String transportDirName = transport.transportDirName();
File stateDir = new File(mBaseStateDir, transportDirName);
File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
if (isPending) {
// We need an init before we can proceed with sending backup data.
// Record that with an entry in our set of pending inits, as well as
// journaling it via creation of a sentinel file.
mPendingInits.add(transportName);
try {
(new FileOutputStream(initPendingFile)).close();
} catch (IOException ioe) {
// Something is badly wrong with our permissions; just try to move on
}
} else {
// No more initialization needed; wipe the journal and reset our state.
initPendingFile.delete();
mPendingInits.remove(transportName);
}
} catch (RemoteException e) {
// can't happen; the transport is local
}
}
// Reset all of our bookkeeping, in response to having been told that
// the backend data has been wiped [due to idle expiry, for example],
// so we must re-upload all saved settings.
@@ -430,7 +514,10 @@ class BackupManagerService extends IBackupManager.Stub {
// Remove all the state files
for (File sf : stateFileDir.listFiles()) {
sf.delete();
// ... but don't touch the needs-init sentinel
if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
sf.delete();
}
}
// Enqueue a new backup of every participant
@@ -455,6 +542,29 @@ class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport);
mTransports.put(name, transport);
}
// If the init sentinel file exists, we need to be sure to perform the init
// as soon as practical. We also create the state directory at registration
// time to ensure it's present from the outset.
try {
String transportName = transport.transportDirName();
File stateDir = new File(mBaseStateDir, transportName);
stateDir.mkdirs();
File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
if (initSentinel.exists()) {
synchronized (mQueueLock) {
mPendingInits.add(transportName);
// TODO: pick a better starting time than now + 1 minute
long delay = 1000 * 60; // one minute, in milliseconds
mAlarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + delay, mRunInitIntent);
}
}
} catch (RemoteException e) {
// can't happen, the transport is local
}
}
// ----- Track installation/removal of packages -----
@@ -581,6 +691,20 @@ class BackupManagerService extends IBackupManager.Stub {
(new PerformClearThread(params.transport, params.packageInfo)).start();
break;
}
case MSG_RUN_INITIALIZE:
{
HashSet<String> queue;
// Snapshot the pending-init queue and work on that
synchronized (mQueueLock) {
queue = new HashSet<String>(mPendingInits);
mPendingInits.clear();
}
(new PerformInitializeThread(queue)).start();
break;
}
}
}
}
@@ -907,7 +1031,6 @@ class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
// can't happen; the transport is local
}
mStateDir.mkdirs();
}
@Override
@@ -1206,7 +1329,6 @@ class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) {
// can't happen; the transport is local
}
mStateDir.mkdirs();
}
@Override
@@ -1552,6 +1674,80 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
class PerformInitializeThread extends Thread {
HashSet<String> mQueue;
PerformInitializeThread(HashSet<String> transportNames) {
mQueue = transportNames;
}
@Override
public void run() {
int status;
try {
for (String transportName : mQueue) {
IBackupTransport transport = getTransport(transportName);
if (transport == null) {
Log.e(TAG, "Requested init for " + transportName + " but not found");
continue;
}
status = BackupConstants.TRANSPORT_OK;
File stateDir = null;
Log.i(TAG, "Device init on " + transport.transportDirName());
stateDir = new File(mBaseStateDir, transport.transportDirName());
status = transport.initializeDevice();
if (status != BackupConstants.TRANSPORT_OK) {
Log.e(TAG, "Error from initializeDevice: " + status);
}
if (status == BackupConstants.TRANSPORT_OK) {
status = transport.finishBackup();
}
// Okay, the wipe really happened. Clean up our local bookkeeping.
if (status == BackupConstants.TRANSPORT_OK) {
resetBackupState(stateDir);
synchronized (mQueueLock) {
recordInitPendingLocked(false, transportName);
}
}
// If this didn't work, requeue this one and try again
// after a suitable interval
if (status != BackupConstants.TRANSPORT_OK) {
Log.i(TAG, "Device init failed");
synchronized (mQueueLock) {
recordInitPendingLocked(true, transportName);
}
// do this via another alarm to make sure of the wakelock states
long delay = transport.requestBackupTime();
if (DEBUG) Log.w(TAG, "init failed on "
+ transportName + " resched in " + delay);
mAlarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + delay, mRunInitIntent);
} else {
// success!
Log.i(TAG, "Device init successful");
}
}
} catch (RemoteException e) {
// can't happen; the transports are local
} catch (Exception e) {
Log.e(TAG, "Unexpected error performing init", e);
} finally {
// Done; indicate that we're finished and release the wakelock
synchronized (mQueueLock) {
mInitInProgress = false;
}
mWakelock.release();
}
}
}
// ----- IBackupManager binder interface -----
@@ -1694,6 +1890,8 @@ class BackupManagerService extends IBackupManager.Stub {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
Log.i(TAG, "Backup enabled => " + enable);
boolean wasEnabled = mEnabled;
synchronized (this) {
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -1707,7 +1905,27 @@ class BackupManagerService extends IBackupManager.Stub {
startBackupAlarmsLocked(BACKUP_INTERVAL);
} else if (!enable) {
// No longer enabled, so stop running backups
if (DEBUG) Log.i(TAG, "Opting out of backup");
mAlarmManager.cancel(mRunBackupIntent);
// This also constitutes an opt-out, so we wipe any data for
// this device from the backend. We start that process with
// an alarm in order to guarantee wakelock states.
if (wasEnabled && mProvisioned) {
// NOTE: we currently flush every registered transport, not just
// the currently-active one.
HashSet<String> allTransports;
synchronized (mTransports) {
allTransports = new HashSet<String>(mTransports.keySet());
}
// build the set of transports for which we are posting an init
for (String transport : allTransports) {
recordInitPendingLocked(true, transport);
}
mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
mRunInitIntent);
}
}
}
}