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));
                 }