Add provisioning code, and use it to set up policies.
Also includes a tweak to return protocol version during
validation, and some incomplete comment cleanup.
Change-Id: I7e609e1a3274a9e71a45349a7facc0d5c88c9565
diff --git a/src/com/android/exchange/service/EasAccountSyncHandler.java b/src/com/android/exchange/service/EasAccountSyncHandler.java
index 67d98dd..0eaf857 100644
--- a/src/com/android/exchange/service/EasAccountSyncHandler.java
+++ b/src/com/android/exchange/service/EasAccountSyncHandler.java
@@ -32,6 +32,16 @@
super(context, contentResolver, account, null, syncExtras, syncResult);
}
+ private boolean tryProvision() {
+ try {
+ final EasAccountValidator validator = new EasAccountValidator(mContext, mHostAuth);
+ return validator.tryProvision(getClientConnectionManager(mHostAuth), mAccount);
+ } catch (final IOException e) {
+
+ }
+ return false;
+ }
+
@Override
public SyncStatus performSync() {
boolean needsResync;
@@ -63,15 +73,21 @@
} catch (final CommandStatusException e) {
final int status = e.mStatus;
if (CommandStatus.isNeedsProvisioning(status)) {
- // TODO: Attempt provisioning.
- return SyncStatus.FAILURE_SECURITY;
+ if (!tryProvision()) {
+ return SyncStatus.FAILURE_SECURITY;
+ } else {
+ return SyncStatus.SUCCESS;
+ }
}
return SyncStatus.FAILURE_OTHER;
}
}
} else if (EasResponse.isProvisionError(code)) {
- // TODO: Attempt provisioning.
- return SyncStatus.FAILURE_SECURITY;
+ if (!tryProvision()) {
+ return SyncStatus.FAILURE_SECURITY;
+ } else {
+ return SyncStatus.SUCCESS;
+ }
} else if (EasResponse.isAuthError(code)) {
return SyncStatus.FAILURE_LOGIN;
} else if (EasResponse.isRedirectError(code)) {
diff --git a/src/com/android/exchange/service/EasAccountValidator.java b/src/com/android/exchange/service/EasAccountValidator.java
index 720f8f6..3322b8b 100644
--- a/src/com/android/exchange/service/EasAccountValidator.java
+++ b/src/com/android/exchange/service/EasAccountValidator.java
@@ -9,7 +9,9 @@
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth;
+import com.android.emailcommon.provider.Policy;
import com.android.emailcommon.service.EmailServiceProxy;
+import com.android.emailcommon.service.PolicyServiceProxy;
import com.android.emailcommon.utility.EmailClientConnectionManager;
import com.android.exchange.CommandStatusException;
import com.android.exchange.CommandStatusException.CommandStatus;
@@ -247,6 +249,12 @@
Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) ? EAS_12_POLICY_TYPE : EAS_2_POLICY_TYPE;
}
+ private void acknowledgeRemoteWipe(final EmailClientConnectionManager connectionManager,
+ final Account account, final String tempKey)
+ throws IOException {
+ acknowledgeProvisionImpl(connectionManager, account, tempKey, PROVISION_STATUS_OK, true);
+ }
+
private String acknowledgeProvision(final EmailClientConnectionManager connectionManager,
final Account account, final String tempKey, final String result)
throws IOException {
@@ -358,6 +366,73 @@
return null;
}
+
+ public boolean tryProvision(final EmailClientConnectionManager connectionManager,
+ final Account account) throws IOException {
+ mProtocolVersion = account.mProtocolVersion;
+ mProtocolVersionDouble = Eas.getProtocolVersionDouble(mProtocolVersion);
+ // First, see if provisioning is even possible, i.e. do we support the policies required
+ // by the server
+ ProvisionParser pp = canProvision(connectionManager, account);
+ if (pp == null) return false;
+ // Get the policies from ProvisionParser
+ Policy policy = pp.getPolicy();
+ Policy oldPolicy = null;
+ // Grab the old policy (if any)
+ if (account.mPolicyKey > 0) {
+ oldPolicy = Policy.restorePolicyWithId(mContext, account.mPolicyKey);
+ }
+ // Update the account with a null policyKey (the key we've gotten is
+ // temporary and cannot be used for syncing)
+ PolicyServiceProxy.setAccountPolicy(mContext, account.mId, policy, null);
+ // Make sure mAccount is current (with latest policy key)
+ account.refresh(mContext);
+ if (pp.getRemoteWipe()) {
+ // We've gotten a remote wipe command
+ LogUtils.i(TAG, "!!! Remote wipe request received");
+ // Start by setting the account to security hold
+ PolicyServiceProxy.setAccountHoldFlag(mContext, account, true);
+
+ // First, we've got to acknowledge it, but wrap the wipe in try/catch so that
+ // we wipe the device regardless of any errors in acknowledgment
+ try {
+ LogUtils.i(TAG, "!!! Acknowledging remote wipe to server");
+ acknowledgeRemoteWipe(connectionManager, account, pp.getSecuritySyncKey());
+ } catch (Exception e) {
+ // Because remote wipe is such a high priority task, we don't want to
+ // circumvent it if there's an exception in acknowledgment
+ }
+ // Then, tell SecurityPolicy to wipe the device
+ LogUtils.i(TAG, "!!! Executing remote wipe");
+ PolicyServiceProxy.remoteWipe(mContext);
+ return false;
+ } else if (pp.hasSupportablePolicySet() && PolicyServiceProxy.isActive(mContext, policy)) {
+ // See if the required policies are in force; if they are, acknowledge the policies
+ // to the server and get the final policy key
+ // NOTE: For EAS 14.0, we already have the acknowledgment in the ProvisionParser
+ String securitySyncKey;
+ if (mProtocolVersionDouble == Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) {
+ securitySyncKey = pp.getSecuritySyncKey();
+ } else {
+ securitySyncKey = acknowledgeProvision(connectionManager, account,
+ pp.getSecuritySyncKey(), PROVISION_STATUS_OK);
+ }
+ if (securitySyncKey != null) {
+ // If attachment policies have changed, fix up any affected attachment records
+ if (oldPolicy != null) {
+ if ((oldPolicy.mDontAllowAttachments != policy.mDontAllowAttachments) ||
+ (oldPolicy.mMaxAttachmentSize != policy.mMaxAttachmentSize)) {
+ Policy.setAttachmentFlagsForNewPolicy(mContext, account, policy);
+ }
+ }
+ // Write the final policy key to the Account and say we've been successful
+ PolicyServiceProxy.setAccountPolicy(mContext, account.mId, policy, securitySyncKey);
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Perform the actual validation.
* @return The validation response.
@@ -391,6 +466,8 @@
return bundle;
}
account.mProtocolVersion = mProtocolVersion;
+ bundle.putString(EmailServiceProxy.VALIDATE_BUNDLE_PROTOCOL_VERSION,
+ mProtocolVersion);
resultCode = doFolderSync(connectionManager, account);
} catch (final CommandStatusException e) {
final int status = e.mStatus;
diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java
index 5f24af0..3a72fa4 100644
--- a/src/com/android/exchange/service/EmailSyncAdapterService.java
+++ b/src/com/android/exchange/service/EmailSyncAdapterService.java
@@ -45,6 +45,13 @@
import android.os.IBinder;
import android.util.Log;
+/**
+ * Service for communicating with Exchange servers. There are three main parts of this class:
+ * TODO: Flesh out these comments.
+ * 1) An {@link AbstractThreadedSyncAdapter} to handle actually performing syncs.
+ * 2) Bookkeeping for running Ping requests, which handles push notifications.
+ * 3) An {@link IEmailService} Stub to handle RPC from the UI.
+ */
public class EmailSyncAdapterService extends AbstractSyncAdapterService {
private static final String TAG = "EAS EmailSyncAdapterService";
@@ -165,10 +172,8 @@
// higher priority than ping (i.e. a ping can't start while a sync is pending)
// and only one ping can run at a time.
EasPingSyncHandler pingHandler = new EasPingSyncHandler(context, account, this);
- // TODO: error handling for pings that never took flight?
mPingHandlers.put(accountId, pingHandler);
// Whenever we have a running ping, make sure this service stays running.
- // TODO: make sure this is the right way to do this.
final EmailSyncAdapterService service = EmailSyncAdapterService.this;
service.startService(new Intent(service, EmailSyncAdapterService.class));
}