Bluetooth : Modify build & parse vcard

If the contacts have "p"or "w" instead of "," or ";" when some carkits
are receiving them from andro id OS, some catkits cannot parse the
contacts.

Bug: 5178723
Change-Id: I536ca57aff561fb0637448bcddfd53a6ba8b28ef
diff --git a/java/com/android/vcard/VCardBuilder.java b/java/com/android/vcard/VCardBuilder.java
index ae778c5..969ac1b 100644
--- a/java/com/android/vcard/VCardBuilder.java
+++ b/java/com/android/vcard/VCardBuilder.java
@@ -31,6 +31,7 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
@@ -846,17 +847,39 @@
                         appendTelLine(type, label, phoneNumber, isPrimary);
                     }
                 } else {
-                    final List<String> phoneNumberList = splitAndTrimPhoneNumbers(phoneNumber);
+                    final List<String> phoneNumberList = splitPhoneNumbers(phoneNumber);
                     if (phoneNumberList.isEmpty()) {
                         continue;
                     }
                     phoneLineExists = true;
                     for (String actualPhoneNumber : phoneNumberList) {
                         if (!phoneSet.contains(actualPhoneNumber)) {
-                            final int phoneFormat = VCardUtils.getPhoneNumberFormat(mVCardType);
-                            String formatted =
-                                    PhoneNumberUtilsPort.formatNumber(
-                                            actualPhoneNumber, phoneFormat);
+                            // 'p' and 'w' are the standard characters for pause and wait
+                            // (see RFC 3601)
+                            // so use those when exporting phone numbers via vCard.
+                            String numberWithControlSequence = actualPhoneNumber
+                                    .replace(PhoneNumberUtils.PAUSE, 'p')
+                                    .replace(PhoneNumberUtils.WAIT, 'w');
+                            String formatted;
+                            // TODO: remove this code and relevant test cases. vCard and any other
+                            // codes using it shouldn't rely on the formatter here.
+                            if (TextUtils.equals(numberWithControlSequence, actualPhoneNumber)) {
+                                StringBuilder digitsOnlyBuilder = new StringBuilder();
+                                final int length = actualPhoneNumber.length();
+                                for (int i = 0; i < length; i++) {
+                                    final char ch = actualPhoneNumber.charAt(i);
+                                    if (Character.isDigit(ch) || ch == '+') {
+                                        digitsOnlyBuilder.append(ch);
+                                    }
+                                }
+                                final int phoneFormat =
+                                        VCardUtils.getPhoneNumberFormat(mVCardType);
+                                formatted = PhoneNumberUtilsPort.formatNumber(
+                                        digitsOnlyBuilder.toString(), phoneFormat);
+                            } else {
+                                // Be conservative.
+                                formatted = numberWithControlSequence;
+                            }
 
                             // In vCard 4.0, value type must be "a single URI value",
                             // not just a phone number. (Based on vCard 4.0 rev.13)
@@ -909,24 +932,23 @@
      * Do not call this method when trimming is inappropriate for its receivers.
      * </p>
      */
-    private List<String> splitAndTrimPhoneNumbers(final String phoneNumber) {
+    private List<String> splitPhoneNumbers(final String phoneNumber) {
         final List<String> phoneList = new ArrayList<String>();
 
         StringBuilder builder = new StringBuilder();
         final int length = phoneNumber.length();
         for (int i = 0; i < length; i++) {
             final char ch = phoneNumber.charAt(i);
-            if (Character.isDigit(ch) || ch == '+') {
-                builder.append(ch);
-            } else if ((ch == ';' || ch == '\n') && builder.length() > 0) {
+            if (ch == '\n' && builder.length() > 0) {
                 phoneList.add(builder.toString());
                 builder = new StringBuilder();
+            } else {
+                builder.append(ch);
             }
         }
         if (builder.length() > 0) {
             phoneList.add(builder.toString());
         }
-
         return phoneList;
     }
 
diff --git a/java/com/android/vcard/VCardEntry.java b/java/com/android/vcard/VCardEntry.java
index 4edd559..255697b 100644
--- a/java/com/android/vcard/VCardEntry.java
+++ b/java/com/android/vcard/VCardEntry.java
@@ -39,6 +39,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -1766,21 +1767,37 @@
             mPhoneList = new ArrayList<PhoneData>();
         }
         final StringBuilder builder = new StringBuilder();
-        final String trimed = data.trim();
+        final String trimmed = data.trim();
         final String formattedNumber;
         if (type == Phone.TYPE_PAGER || VCardConfig.refrainPhoneNumberFormatting(mVCardType)) {
-            formattedNumber = trimed;
+            formattedNumber = trimmed;
         } else {
-            final int length = trimed.length();
+            // TODO: from the view of vCard spec these auto conversions should be removed.
+            // Note that some other codes (like the phone number formatter) or modules expect this
+            // auto conversion (bug 5178723), so just omitting this code won't be preferable enough
+            // (bug 4177894)
+            boolean hasPauseOrWait = false;
+            final int length = trimmed.length();
             for (int i = 0; i < length; i++) {
-                char ch = trimed.charAt(i);
-                if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
+                char ch = trimmed.charAt(i);
+                // See RFC 3601 and docs for PhoneNumberUtils for more info.
+                if (ch == 'p' || ch == 'P') {
+                    builder.append(PhoneNumberUtils.PAUSE);
+                    hasPauseOrWait = true;
+                } else if (ch == 'w' || ch == 'W') {
+                    builder.append(PhoneNumberUtils.WAIT);
+                    hasPauseOrWait = true;
+                } else if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
                     builder.append(ch);
                 }
             }
-
-            final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
-            formattedNumber = PhoneNumberUtilsPort.formatNumber(builder.toString(), formattingType);
+            if (!hasPauseOrWait) {
+                final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
+                formattedNumber = PhoneNumberUtilsPort.formatNumber(
+                        builder.toString(), formattingType);
+            } else {
+                formattedNumber = builder.toString();
+            }
         }
         PhoneData phoneData = new PhoneData(formattedNumber, type, label, isPrimary);
         mPhoneList.add(phoneData);
diff --git a/tests/res/raw/v30_pause_wait.vcf b/tests/res/raw/v30_pause_wait.vcf
new file mode 100644
index 0000000..e8fc2e9
--- /dev/null
+++ b/tests/res/raw/v30_pause_wait.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD
+VERSION:3.0
+FN:Pause Wait
+N:Pause;Wait;;;
+TEL:p1234p5678w9
+END:VCARD
diff --git a/tests/src/com/android/vcard/tests/VCardExporterTests.java b/tests/src/com/android/vcard/tests/VCardExporterTests.java
index 76f4a8f..42f9445 100644
--- a/tests/src/com/android/vcard/tests/VCardExporterTests.java
+++ b/tests/src/com/android/vcard/tests/VCardExporterTests.java
@@ -19,8 +19,8 @@
 import com.android.vcard.VCardConfig;
 import com.android.vcard.tests.testutils.ContactEntry;
 import com.android.vcard.tests.testutils.PropertyNodesVerifierElem;
-import com.android.vcard.tests.testutils.VCardTestsBase;
 import com.android.vcard.tests.testutils.PropertyNodesVerifierElem.TypeSet;
+import com.android.vcard.tests.testutils.VCardTestsBase;
 
 import android.content.ContentValues;
 import android.provider.ContactsContract.CommonDataKinds.Email;
@@ -1209,8 +1209,8 @@
         ContactEntry entry = mVerifier.addInputEntry();
         entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
                 .put(Phone.TYPE, Phone.TYPE_HOME)
-                .put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo);"
-                        + "777-888-9999 (Chicago);111-222-3333 (Miami)");
+                .put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo)\n"
+                        + "777-888-9999 (Chicago)\n111-222-3333 (Miami)");
         mVerifier.addPropertyNodesVerifierElemWithEmptyName()
                 .addExpectedNode("TEL", "111-222-3333", new TypeSet("HOME"))
                 .addExpectedNode("TEL", "444-555-5666", new TypeSet("HOME"))
@@ -1292,6 +1292,22 @@
                 .addExpectedNode("IMPP", "sip:android@example.com");
     }
 
+    public void testPauseAndWaitConversionV30() {
+        mVerifier.initForExportTest(V30);
+        final ContactEntry entry = mVerifier.addInputEntry();
+        // Insert numbers with PAUSE (',' internally, 'p' for outside) and
+        // WAIT (';' internally, 'w' for outside)
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_HOME)
+                .put(Phone.NUMBER, "111,222;333");
+        mVerifier.addLineVerifierElem()
+                .addExpected("N:")
+                .addExpected("FN:")
+                .addExpected("TEL;TYPE=HOME:111p222w333");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "111p222w333", new TypeSet("HOME"));
+    }
+
     public void testSipAddressV40() {
         mVerifier.initForExportTest(V40);
         final ContactEntry entry = mVerifier.addInputEntry();
diff --git a/tests/src/com/android/vcard/tests/VCardImporterTests.java b/tests/src/com/android/vcard/tests/VCardImporterTests.java
index e1efec4..e1843a5 100644
--- a/tests/src/com/android/vcard/tests/VCardImporterTests.java
+++ b/tests/src/com/android/vcard/tests/VCardImporterTests.java
@@ -1239,4 +1239,26 @@
         // Smoke test.
         elem.addExpected("custom_mime4").put("data1", "z");
     }
+
+    public void testPauseWaitV30_Parse() {
+        mVerifier.initForImportTest(V30, R.raw.v30_pause_wait);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("FN", "Pause Wait")
+                .addExpectedNodeWithOrder("N", "Pause;Wait;;;",
+                        Arrays.asList("Pause", "Wait", "", "", ""))
+                .addExpectedNodeWithOrder("TEL", "p1234p5678w9");
+     }
+
+    public void testPauseWaitV30() {
+        mVerifier.initForImportTest(V30, R.raw.v30_pause_wait);
+        final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "Pause")
+                .put(StructuredName.GIVEN_NAME, "Wait")
+                .put(StructuredName.DISPLAY_NAME, "Pause Wait");
+        // See PhoneNumberUtils in Android SDK.
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_HOME)
+                .put(Phone.NUMBER, ",1234,5678;9");
+    }
 }