Implement basic EAS 12.1 functionality

* Support required protocol changes
* Handle new security policies based on current device capabilities

Change-Id: Id1d629d41d957911344e6c503d28418f5e7e1386
diff --git a/src/com/android/exchange/Eas.java b/src/com/android/exchange/Eas.java
index 8e561e7..2fa162b 100644
--- a/src/com/android/exchange/Eas.java
+++ b/src/com/android/exchange/Eas.java
@@ -45,6 +45,8 @@
     public static final double SUPPORTED_PROTOCOL_EX2003_DOUBLE = 2.5;
     public static final String SUPPORTED_PROTOCOL_EX2007 = "12.0";
     public static final double SUPPORTED_PROTOCOL_EX2007_DOUBLE = 12.0;
+    public static final String SUPPORTED_PROTOCOL_EX2007_SP1 = "12.1";
+    public static final double SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE = 12.1;
     public static final String DEFAULT_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_EX2003;
 
     // From EAS spec
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index 82a375f..0f9639a 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -352,7 +352,8 @@
         // Find the most recent version we support
         for (String version: supportedVersionsArray) {
             if (version.equals(Eas.SUPPORTED_PROTOCOL_EX2003) ||
-                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2007)) {
+                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2007) ||
+                    version.equals(Eas.SUPPORTED_PROTOCOL_EX2007_SP1)) {
                 ourVersion = version;
             }
         }
@@ -1979,9 +1980,12 @@
             userLog("sync, sending ", className, " syncKey: ", syncKey);
             s.start(Tags.SYNC_SYNC)
                 .start(Tags.SYNC_COLLECTIONS)
-                .start(Tags.SYNC_COLLECTION)
-                .data(Tags.SYNC_CLASS, className)
-                .data(Tags.SYNC_SYNC_KEY, syncKey)
+                .start(Tags.SYNC_COLLECTION);
+            // The "Class" element is removed in EAS 12.1 and later versions
+            if (mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) {
+                s.data(Tags.SYNC_CLASS, className);
+            }
+            s.data(Tags.SYNC_SYNC_KEY, syncKey)
                 .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId);
 
             // Start with the default timeout
diff --git a/src/com/android/exchange/adapter/ProvisionParser.java b/src/com/android/exchange/adapter/ProvisionParser.java
index af1ecdd..5304639 100644
--- a/src/com/android/exchange/adapter/ProvisionParser.java
+++ b/src/com/android/exchange/adapter/ProvisionParser.java
@@ -56,7 +56,7 @@
         return mRemoteWipe;
     }
 
-    public void parseProvisionDocWbxml() throws IOException {
+    private void parseProvisionDocWbxml() throws IOException {
         int minPasswordLength = 0;
         int passwordMode = PolicySet.PASSWORD_MODE_NONE;
         int maxPasswordFails = 0;
@@ -64,6 +64,7 @@
         boolean canSupport = true;
 
         while (nextTag(Tags.PROVISION_EAS_PROVISION_DOC) != END) {
+            boolean supported = true;
             switch (tag) {
                 case Tags.PROVISION_DEVICE_PASSWORD_ENABLED:
                     if (getValueInt() == 1) {
@@ -92,10 +93,28 @@
                     // Hint: I haven't seen any that's more specific than "simple"
                     getValue();
                     break;
-                // The following policy, if false, can't be supported at the moment
+                // The following policies, if false, can't be supported at the moment
                 case Tags.PROVISION_ATTACHMENTS_ENABLED:
+                case Tags.PROVISION_ALLOW_STORAGE_CARD:
+                case Tags.PROVISION_ALLOW_CAMERA:
+                case Tags.PROVISION_ALLOW_UNSIGNED_APPLICATIONS:
+                case Tags.PROVISION_ALLOW_UNSIGNED_INSTALLATION_PACKAGES:
+                case Tags.PROVISION_ALLOW_WIFI:
+                case Tags.PROVISION_ALLOW_TEXT_MESSAGING:
+                case Tags.PROVISION_ALLOW_POP_IMAP_EMAIL:
+                case Tags.PROVISION_ALLOW_IRDA:
+                case Tags.PROVISION_ALLOW_HTML_EMAIL:
+                case Tags.PROVISION_ALLOW_BROWSER:
+                case Tags.PROVISION_ALLOW_CONSUMER_EMAIL:
+                case Tags.PROVISION_ALLOW_INTERNET_SHARING:
                     if (getValueInt() == 0) {
-                        canSupport = false;
+                        supported = false;
+                    }
+                    break;
+                // Bluetooth: 0 = no bluetooth; 1 = only hands-free; 2 = allowed
+                case Tags.PROVISION_ALLOW_BLUETOOTH:
+                    if (getValueInt() != 2) {
+                        supported = false;
                     }
                     break;
                 // The following policies, if true, can't be supported at the moment
@@ -103,14 +122,68 @@
                 case Tags.PROVISION_PASSWORD_RECOVERY_ENABLED:
                 case Tags.PROVISION_DEVICE_PASSWORD_EXPIRATION:
                 case Tags.PROVISION_DEVICE_PASSWORD_HISTORY:
-                case Tags.PROVISION_MAX_ATTACHMENT_SIZE:
+                case Tags.PROVISION_REQUIRE_DEVICE_ENCRYPTION:
+                case Tags.PROVISION_REQUIRE_SIGNED_SMIME_MESSAGES:
+                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) {
-                        canSupport = false;
+                        supported = false;
+                    }
+                    break;
+                // The following, if greater than zero, can't be supported at the moment
+                case Tags.PROVISION_MAX_ATTACHMENT_SIZE:
+                    if (getValueInt() > 0) {
+                        supported = false;
+                    }
+                    break;
+                // Complex character setting is only used if we're in "strong" (alphanumeric) mode
+                case Tags.PROVISION_MIN_DEVICE_PASSWORD_COMPLEX_CHARS:
+                    if ((passwordMode == PolicySet.PASSWORD_MODE_STRONG) && (getValueInt() > 0)) {
+                        supported = false;
+                    }
+                    break;
+                // The following policies are moot; they allow functionality that we don't support
+                case Tags.PROVISION_ALLOW_DESKTOP_SYNC:
+                case Tags.PROVISION_ALLOW_SMIME_ENCRYPTION_NEGOTIATION:
+                case Tags.PROVISION_ALLOW_SMIME_SOFT_CERTS:
+                case Tags.PROVISION_ALLOW_REMOTE_DESKTOP:
+                    skipTag();
+                    break;
+                // We don't handle approved/unapproved application lists
+                case Tags.PROVISION_UNAPPROVED_IN_ROM_APPLICATION_LIST:
+                case Tags.PROVISION_APPROVED_APPLICATION_LIST:
+                    // Parse and throw away the content
+                    if (specifiesApplications(tag)) {
+                        supported = false;
+                    }
+                    break;
+                // NOTE: We can support these entirely within the email application if we choose
+                case Tags.PROVISION_MAX_CALENDAR_AGE_FILTER:
+                case Tags.PROVISION_MAX_EMAIL_AGE_FILTER:
+                    // 0 indicates no specified filter
+                    if (getValueInt() != 0) {
+                        supported = false;
+                    }
+                    break;
+                // NOTE: We can support these entirely within the email application if we choose
+                case Tags.PROVISION_MAX_EMAIL_BODY_TRUNCATION_SIZE:
+                case Tags.PROVISION_MAX_EMAIL_HTML_BODY_TRUNCATION_SIZE:
+                    String value = getValue();
+                    // -1 indicates no required truncation
+                    if (!value.equals("-1")) {
+                        supported = false;
                     }
                     break;
                 default:
                     skipTag();
             }
+
+            if (!supported) {
+                log("** Policy not supported");
+                canSupport = false;
+            }
         }
 
         if (canSupport) {
@@ -119,6 +192,27 @@
         }
     }
 
+    /**
+     * Return whether or not either of the application list tags specifies any applications
+     * @param endTag the tag whose children we're walking through
+     * @return whether any applications were specified (by name or by hash)
+     * @throws IOException
+     */
+    private boolean specifiesApplications(int endTag) throws IOException {
+        boolean specifiesApplications = false;
+        while (nextTag(endTag) != END) {
+            switch (tag) {
+                case Tags.PROVISION_APPLICATION_NAME:
+                case Tags.PROVISION_HASH:
+                    specifiesApplications = true;
+                    break;
+                default:
+                    skipTag();
+            }
+        }
+        return specifiesApplications;
+    }
+
     class ShadowPolicySet {
         int mMinPasswordLength = 0;
         int mPasswordMode = PolicySet.PASSWORD_MODE_NONE;
@@ -126,7 +220,7 @@
         int mMaxScreenLockTime = 0;
     }
 
-    public void parseProvisionDocXml(String doc) throws IOException {
+    /*package*/ void parseProvisionDocXml(String doc) throws IOException {
         ShadowPolicySet sps = new ShadowPolicySet();
 
         try {
@@ -154,7 +248,7 @@
     /**
      * Return true if password is required; otherwise false.
      */
-    boolean parseSecurityPolicy(XmlPullParser parser, ShadowPolicySet sps)
+    private boolean parseSecurityPolicy(XmlPullParser parser, ShadowPolicySet sps)
             throws XmlPullParserException, IOException {
         boolean passwordRequired = true;
         while (true) {
@@ -177,7 +271,7 @@
         return passwordRequired;
     }
 
-    void parseCharacteristic(XmlPullParser parser, ShadowPolicySet sps)
+    private void parseCharacteristic(XmlPullParser parser, ShadowPolicySet sps)
             throws XmlPullParserException, IOException {
         boolean enforceInactivityTimer = true;
         while (true) {
@@ -219,7 +313,7 @@
         }
     }
 
-    void parseRegistry(XmlPullParser parser, ShadowPolicySet sps)
+    private void parseRegistry(XmlPullParser parser, ShadowPolicySet sps)
             throws XmlPullParserException, IOException {
       while (true) {
           int type = parser.nextTag();
@@ -234,7 +328,7 @@
       }
     }
 
-    void parseWapProvisioningDoc(XmlPullParser parser, ShadowPolicySet sps)
+    private void parseWapProvisioningDoc(XmlPullParser parser, ShadowPolicySet sps)
             throws XmlPullParserException, IOException {
         while (true) {
             int type = parser.nextTag();
@@ -258,7 +352,7 @@
         }
     }
 
-    public void parseProvisionData() throws IOException {
+    private void parseProvisionData() throws IOException {
         while (nextTag(Tags.PROVISION_DATA) != END) {
             if (tag == Tags.PROVISION_EAS_PROVISION_DOC) {
                 parseProvisionDocWbxml();
@@ -268,7 +362,7 @@
         }
     }
 
-    public void parsePolicy() throws IOException {
+    private void parsePolicy() throws IOException {
         String policyType = null;
         while (nextTag(Tags.PROVISION_POLICY) != END) {
             switch (tag) {
@@ -297,7 +391,7 @@
         }
     }
 
-    public void parsePolicies() throws IOException {
+    private void parsePolicies() throws IOException {
         while (nextTag(Tags.PROVISION_POLICIES) != END) {
             if (tag == Tags.PROVISION_POLICY) {
                 parsePolicy();
diff --git a/src/com/android/exchange/adapter/Tags.java b/src/com/android/exchange/adapter/Tags.java
index ef70981..2745e33 100644
--- a/src/com/android/exchange/adapter/Tags.java
+++ b/src/com/android/exchange/adapter/Tags.java
@@ -596,7 +596,7 @@
             "MinDevicePasswordComplexCharacters", "AllowWiFi", "AllowTextMessaging",
             "AllowPOPIMAPEmail", "AllowBluetooth", "AllowIrDA", "RequireManualSyncWhenRoaming",
             "AllowDesktopSync",
-            "MaxCalendarAgeFilder", "AllowHTMLEmail", "MaxEmailAgeFilder",
+            "MaxCalendarAgeFilder", "AllowHTMLEmail", "MaxEmailAgeFilter",
             "MaxEmailBodyTruncationSize", "MaxEmailHTMLBodyTruncationSize",
             "RequireSignedSMIMEMessages", "RequireEncryptedSMIMEMessages",
             "RequireSignedSMIMEAlgorithm", "RequireEncryptionSMIMEAlgorithm",