Enforce "manual sync only when roaming"

* This policy is enforced by ExchangeService, regardless of system
  support

Change-Id: I1009e8f89b2833c5f35c554d00e7795b754dff6c
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index d887b24..c943be6 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -1864,6 +1864,8 @@
             }
 
             while (!mStop) {
+                // If we're not allowed to sync (e.g. roaming policy), leave now
+                if (!ExchangeService.canAutoSync(mAccount)) return;
                 userLog("Sending Account syncKey: ", mAccount.mSyncKey);
                 Serializer s = new Serializer();
                 s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
@@ -2162,6 +2164,12 @@
                         int code = resp.getStatus();
                         userLog("Ping response: ", code);
 
+                        // If we're not allowed to sync (e.g. roaming policy), terminate gracefully
+                        // now; otherwise we might start a sync based on the response
+                        if (!ExchangeService.canAutoSync(mAccount)) {
+                            mStop = true;
+                        }
+
                         // Return immediately if we've been asked to stop during the ping
                         if (mStop) {
                             userLog("Stopping pingLoop");
diff --git a/src/com/android/exchange/ExchangeService.java b/src/com/android/exchange/ExchangeService.java
index 3b27845..2c4ec99 100644
--- a/src/com/android/exchange/ExchangeService.java
+++ b/src/com/android/exchange/ExchangeService.java
@@ -28,6 +28,7 @@
 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
 import com.android.emailcommon.provider.EmailContent.Message;
 import com.android.emailcommon.provider.EmailContent.SyncColumns;
+import com.android.emailcommon.provider.Policy;
 import com.android.emailcommon.service.AccountServiceProxy;
 import com.android.emailcommon.service.EmailServiceProxy;
 import com.android.emailcommon.service.EmailServiceStatus;
@@ -239,6 +240,8 @@
     private ConnectivityReceiver mConnectivityReceiver = null;
     private ConnectivityReceiver mBackgroundDataSettingReceiver = null;
     private volatile boolean mBackgroundData = true;
+    // The most current NetworkInfo (from ConnectivityManager)
+    private NetworkInfo mNetworkInfo;
 
     // Callbacks as set up via setCallback
     private RemoteCallbackList<IEmailServiceCallback> mCallbackList =
@@ -1716,6 +1719,7 @@
         while (!sStop) {
             NetworkInfo info = cm.getActiveNetworkInfo();
             if (info != null) {
+                mNetworkInfo = info;
                 // We're done if there's an active network
                 if (waiting) {
                     // If we've been waiting, release any I/O error holds
@@ -2098,6 +2102,35 @@
         return false;
     }
 
+    /**
+     * Determine whether the account is allowed to sync automatically, as opposed to manually, based
+     * on whether the "require manual sync when roaming" policy is in force and applicable
+     * @param account the account
+     * @return whether or not the account can sync automatically
+     */
+    /*package*/ static boolean canAutoSync(Account account) {
+        ExchangeService exchangeService = INSTANCE;
+        if (exchangeService == null) return false;
+        NetworkInfo networkInfo = exchangeService.mNetworkInfo;
+
+        // Enforce manual sync only while roaming here
+        long policyKey = account.mPolicyKey;
+        // Quick exit from this check
+        if ((policyKey != 0) && (networkInfo != null) &&
+                (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE)) {
+            // We'll cache the Policy data here
+            Policy policy = account.mPolicy;
+            if (policy == null) {
+                policy = Policy.restorePolicyWithId(INSTANCE, policyKey);
+                account.mPolicy = policy;
+            }
+            if (policy != null && policy.mRequireManualSyncWhenRoaming && networkInfo.isRoaming()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private long checkMailboxes () {
         // First, see if any running mailboxes have been deleted
         ArrayList<Long> deletedMailboxes = new ArrayList<Long>();
@@ -2168,6 +2201,10 @@
                         new android.accounts.Account(account.mEmailAddress,
                                 Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE);
 
+                    if (!canAutoSync(account)) {
+                        continue;
+                    }
+
                     if (type == Mailbox.TYPE_CONTACTS || type == Mailbox.TYPE_CALENDAR) {
                         // We don't sync these automatically if master auto sync is off
                         if (!masterAutoSync) {
diff --git a/src/com/android/exchange/adapter/ProvisionParser.java b/src/com/android/exchange/adapter/ProvisionParser.java
index 3c32c8b..af805b3 100644
--- a/src/com/android/exchange/adapter/ProvisionParser.java
+++ b/src/com/android/exchange/adapter/ProvisionParser.java
@@ -90,6 +90,7 @@
 
         while (nextTag(Tags.PROVISION_EAS_PROVISION_DOC) != END) {
             boolean tagIsSupported = true;
+            int res = 0;
             switch (tag) {
                 case Tags.PROVISION_DEVICE_PASSWORD_ENABLED:
                     if (getValueInt() == 1) {
@@ -140,7 +141,6 @@
                 case Tags.PROVISION_ALLOW_INTERNET_SHARING:
                     if (getValueInt() == 0) {
                         tagIsSupported = false;
-                        int res = 0;
                         switch(tag) {
                             case Tags.PROVISION_ATTACHMENTS_ENABLED:
                                 res = R.string.policy_dont_allow_attachments;
@@ -212,6 +212,10 @@
                         policy.mRequireEncryptionExternal = true;
                     }
                     break;
+                // Note this policy; we enforce it in ExchangeService
+                case Tags.PROVISION_REQUIRE_MANUAL_SYNC_WHEN_ROAMING:
+                    policy.mRequireManualSyncWhenRoaming = true;
+                    break;
                 // We are allowed to accept policies, regardless of value of this tag
                 // TODO: When we DO support a recovery password, we need to store the value in
                 // the account (so we know to utilize it)
@@ -224,27 +228,12 @@
                 case Tags.PROVISION_REQUIRE_ENCRYPTED_SMIME_MESSAGES:
                 case Tags.PROVISION_REQUIRE_SIGNED_SMIME_ALGORITHM:
                 case Tags.PROVISION_REQUIRE_ENCRYPTION_SMIME_ALGORITHM:
-                case Tags.PROVISION_REQUIRE_MANUAL_SYNC_WHEN_ROAMING:
                     if (getValueInt() == 1) {
                         tagIsSupported = false;
-                        int res = 0;
-                        switch(tag) {
-                            case Tags.PROVISION_REQUIRE_SIGNED_SMIME_ALGORITHM:
-                            case Tags.PROVISION_REQUIRE_ENCRYPTION_SMIME_ALGORITHM:
-                            case Tags.PROVISION_REQUIRE_ENCRYPTED_SMIME_MESSAGES:
-                            case Tags.PROVISION_REQUIRE_SIGNED_SMIME_MESSAGES:
-                                if (!smimeRequired) {
-                                    res = R.string.policy_require_smime;
-                                    smimeRequired = true;
-                                }
-                                break;
-                            case Tags.PROVISION_REQUIRE_MANUAL_SYNC_WHEN_ROAMING:
-                                res = R.string.policy_require_manual_sync_roaming;
-                                policy.mRequireManualSyncWhenRoaming = true;
-                                break;
-                        }
-                        if (res > 0) {
-                            unsupportedList.add(res);
+                        if (!smimeRequired) {
+                            res = R.string.policy_require_smime;
+                            unsupportedList.add(R.string.policy_require_smime);
+                            smimeRequired = true;
                         }
                     }
                     break;