Properly decode a uid from the globalObjId in invites
* Meeting invitations in EAS include a globalObjId. It turns out
that this id is EITHER the actual uid (if Exchange created it)
or a wrapper for the actual uid (if some other client created it)
* To find out which case we're dealing with, we have to look at
the base64 decoded string for the magic "vCal-Uid" substring
* If it's there, we pull the real uid out of the decoded string
* Otherwise, we build a hex strong from the decoded bytes
* Write unit test for this process
Bug: 2598201
Change-Id: I1cc40af6d1e45be44c19465eb8a4c31851ec8157
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index 5cade38..dc4f204 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -45,7 +45,6 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
-import android.util.Base64;
import android.webkit.MimeTypeMap;
import java.io.IOException;
@@ -215,16 +214,8 @@
packedString.put(MeetingInfo.MEETING_LOCATION, getValue());
break;
case Tags.EMAIL_GLOBAL_OBJID:
- // This is lovely; the unique id is a base64 encoded hex string
- String guid = getValue();
- StringBuilder sb = new StringBuilder();
- // First get the decoded base64
- byte[] bytes = Base64.decode(guid, Base64.DEFAULT);
- // Then go through the bytes and write out the hex values as characters
- for (byte b: bytes) {
- Utility.byteToHex(sb, b);
- }
- packedString.put(MeetingInfo.MEETING_UID, sb.toString());
+ packedString.put(MeetingInfo.MEETING_UID,
+ CalendarUtilities.getUidFromGlobalObjId(getValue()));
break;
case Tags.EMAIL_CATEGORIES:
nullParser();
diff --git a/src/com/android/exchange/utility/CalendarUtilities.java b/src/com/android/exchange/utility/CalendarUtilities.java
index 049ca30..35e42e1 100644
--- a/src/com/android/exchange/utility/CalendarUtilities.java
+++ b/src/com/android/exchange/utility/CalendarUtilities.java
@@ -18,6 +18,7 @@
import com.android.email.Email;
import com.android.email.R;
+import com.android.email.Utility;
import com.android.email.mail.Address;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
@@ -1175,6 +1176,40 @@
return -1;
}
+ /**
+ * Return the uid for an event based on its globalObjId
+ * @param globalObjId the base64 encoded String provided by EAS
+ * @return the uid for the calendar event
+ */
+ static public String getUidFromGlobalObjId(String globalObjId) {
+ StringBuilder sb = new StringBuilder();
+ // First get the decoded base64
+ try {
+ byte[] idBytes = Base64.decode(globalObjId, Base64.DEFAULT);
+ String idString = new String(idBytes);
+ // If the base64 decoded string contains the magic substring: "vCal-Uid", then
+ // the actual uid is hidden within; the magic substring is never at the start of the
+ // decoded base64
+ int index = idString.indexOf("vCal-Uid");
+ if (index > 0) {
+ // The uid starts after "vCal-Uidxxxx", where xxxx are padding
+ // characters. And it ends before the last character, which is ascii 0
+ return idString.substring(index + 12, idString.length() - 1);
+ } else {
+ // This is an EAS uid. Go through the bytes and write out the hex
+ // values as characters; this is what we'll need to pass back to EAS
+ // when responding to the invitation
+ for (byte b: idBytes) {
+ Utility.byteToHex(sb, b);
+ }
+ return sb.toString();
+ }
+ } catch (RuntimeException e) {
+ // In the worst of cases (bad format, etc.), we can always return the input
+ return globalObjId;
+ }
+ }
+
static public String buildMessageTextFromEntityValues(Context context,
ContentValues entityValues, StringBuilder sb) {
if (sb == null) {
diff --git a/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java b/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
index 67172f3..20916e7 100644
--- a/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
+++ b/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
@@ -700,6 +700,20 @@
Log.d("TimeZoneGeneration", "No rule: " + nr);
}
}
+
+ public void testGetUidFromGlobalObjId() {
+ // This is a "foreign" uid (from some vCalendar client)
+ String globalObjId = "BAAAAIIA4AB0xbcQGoLgCAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAA" +
+ "HZDYWwtVWlkAQAAADI3NjU1NmRkLTg1MzAtNGZiZS1iMzE0LThiM2JlYTYwMjE0OQA=";
+ String uid = CalendarUtilities.getUidFromGlobalObjId(globalObjId);
+ assertEquals(uid, "276556dd-8530-4fbe-b314-8b3bea602149");
+ // This is a native EAS uid
+ globalObjId =
+ "BAAAAIIA4AB0xbcQGoLgCAAAAADACTu7KbPKAQAAAAAAAAAAEAAAAObgsG6HVt1Fmy+7GlLbGhY=";
+ uid = CalendarUtilities.getUidFromGlobalObjId(globalObjId);
+ assertEquals(uid, "040000008200E00074C5B7101A82E00800000000C0093BBB29B3CA" +
+ "01000000000000000010000000E6E0B06E8756DD459B2FBB1A52DB1A16");
+ }
}
// TODO Planned unit tests