Bug 770785: Delete client record from server when Android Account is deleted. r=rnewman

--HG--
extra : rebase_source : f07c4fb0d7308f1c2742a9ad5f130a3440e0dabf
This commit is contained in:
Nick Alexander 2012-08-30 11:59:34 -07:00
parent 085b3f44ec
commit 266ba38c83
4 changed files with 170 additions and 8 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.config;
import java.net.URI;
import org.mozilla.gecko.sync.GlobalSession;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.net.BaseResource;
import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
import org.mozilla.gecko.sync.net.SyncStorageResponse;
/**
* Bug 770785: when an Android Account is deleted, we need to (try to) delete
* the associated client GUID from the server's clients table.
* <p>
* This class provides a static method to do that.
*/
public class ClientRecordTerminator {
public static final String LOG_TAG = "ClientRecTerminator";
protected ClientRecordTerminator() {
super(); // Stop this class from being instantiated.
}
public static void deleteClientRecord(final String username,
final String password,
final String clusterURL,
final String clientGuid)
throws Exception {
// Would prefer to delegate to SyncConfiguration, but that would proliferate static methods.
final String collection = "clients";
final URI wboURI = new URI(clusterURL + GlobalSession.API_VERSION + "/" + username + "/storage/" + collection + "/" + clientGuid);
// Would prefer to break this out into a self-contained client library.
final SyncStorageRecordRequest r = new SyncStorageRecordRequest(wboURI);
r.delegate = new SyncStorageRequestDelegate() {
@Override
public String credentials() {
return username + ":" + password;
}
@Override
public String ifUnmodifiedSince() {
return null;
}
@Override
public void handleRequestSuccess(SyncStorageResponse response) {
Logger.info(LOG_TAG, "Deleted client record with GUID " + clientGuid + " from server.");
BaseResource.consumeEntity(response);
}
@Override
public void handleRequestFailure(SyncStorageResponse response) {
Logger.warn(LOG_TAG, "Failed to delete client record with GUID " + clientGuid + " from server.");
try {
Logger.warn(LOG_TAG, "Server error message was: " + response.getErrorMessage());
} catch (Exception e) {
// Do nothing.
}
BaseResource.consumeEntity(response);
}
@Override
public void handleRequestError(Exception ex) {
// It could be that we don't have network access when trying
// to remove an Account; not much to be done in this situation.
Logger.error(LOG_TAG, "Got exception trying to delete client record with GUID " + clientGuid + " from server; ignoring.", ex);
}
};
r.delete();
}
}

View File

@ -9,8 +9,12 @@ import java.security.NoSuchAlgorithmException;
import org.mozilla.gecko.sync.GlobalConstants;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.config.AccountPickler;
import org.mozilla.gecko.sync.config.ClientRecordTerminator;
import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
import android.accounts.AbstractAccountAuthenticator;
@ -21,6 +25,7 @@ import android.accounts.NetworkErrorException;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
@ -217,20 +222,97 @@ public class SyncAuthenticatorService extends Service {
* Account disappeared.
*/
@Override
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) throws NetworkErrorException {
public Bundle getAccountRemovalAllowed(final AccountAuthenticatorResponse response, final Account account)
throws NetworkErrorException {
Bundle result = super.getAccountRemovalAllowed(response, account);
if (result != null &&
result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) &&
!result.containsKey(AccountManager.KEY_INTENT)) {
final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
if (result == null ||
!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) ||
result.containsKey(AccountManager.KEY_INTENT)) {
return result;
}
if (removalAllowed) {
final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
if (!removalAllowed) {
return result;
}
// Delete the Account pickle in the background.
ThreadPool.run(new Runnable() {
@Override
public void run() {
Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
"deleting saved pickle file '" + Constants.ACCOUNT_PICKLE_FILENAME + "'.");
AccountPickler.deletePickle(mContext, Constants.ACCOUNT_PICKLE_FILENAME);
try {
AccountPickler.deletePickle(mContext, Constants.ACCOUNT_PICKLE_FILENAME);
} catch (Exception e) {
// This should never happen, but we really don't want to die in a background thread.
Logger.warn(LOG_TAG, "Got exception deleting saved pickle file; ignoring.", e);
}
}
});
// Bug 770785: delete the Account's client record in the background. We want
// to get the Account's data synchronously, though, since it is possible the
// Account object will be invalid by the time the Runnable executes. We
// don't need to worry about accessing prefs too early since deleting the
// Account doesn't remove them -- at least, not yet.
SyncAccountParameters tempParams = null;
try {
tempParams = SyncAccounts.blockingFromAndroidAccountV0(mContext, AccountManager.get(mContext), account);
} catch (Exception e) {
// Do nothing. Null parameters are handled in the Runnable.
}
final SyncAccountParameters params = tempParams;
ThreadPool.run(new Runnable() {
@Override
public void run() {
Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
"deleting client record from server.");
if (params == null || params.username == null || params.password == null) {
Logger.warn(LOG_TAG, "Account parameters were null; not deleting client record from server.");
return;
}
// This is not exactly modular. We need to get some information about
// the account, namely the current clusterURL and client GUID, and we
// extract it by hand. We're not worried about the Account being
// deleted out from under us since the prefs remain even after Account
// deletion.
final String product = GlobalConstants.BROWSER_INTENT_PACKAGE;
final String profile = Constants.DEFAULT_PROFILE;
final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
SharedPreferences prefs;
try {
prefs = Utils.getSharedPreferences(mContext, product, params.username, params.serverURL, profile, version);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Caught exception fetching preferences; not deleting client record from server.", e);
return;
}
final String clientGuid = prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null);
final String clusterURL = prefs.getString(SyncConfiguration.PREF_CLUSTER_URL, null);
if (clientGuid == null) {
Logger.warn(LOG_TAG, "Client GUID was null; not deleting client record from server.");
return;
}
if (clusterURL == null) {
Logger.warn(LOG_TAG, "Cluster URL was null; not deleting client record from server.");
return;
}
try {
ClientRecordTerminator.deleteClientRecord(params.username, params.password, clusterURL, clientGuid);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Got exception deleting client record from server; ignoring.", e);
}
}
});
return result;
}

View File

@ -3,6 +3,7 @@ sync/CollectionKeys.java
sync/CommandProcessor.java
sync/CommandRunner.java
sync/config/AccountPickler.java
sync/config/ClientRecordTerminator.java
sync/config/ConfigurationMigrator.java
sync/CredentialException.java
sync/CredentialsSource.java