1192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta/* 2192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Copyright (C) 2014 The Android Open Source Project 3192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 4192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * you may not use this file except in compliance with the License. 6192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * You may obtain a copy of the License at 7192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 8192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * http://www.apache.org/licenses/LICENSE-2.0 9192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 10192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Unless required by applicable law or agreed to in writing, software 11192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * See the License for the specific language governing permissions and 14192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * limitations under the License. 15192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 16192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 17192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptapackage android.bluetooth.client.map; 18192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 19192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.util.Log; 20192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 21192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardEntry; 22192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardEntryConstructor; 23192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardEntryHandler; 24192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardParser; 25192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardParser_V21; 26192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardParser_V30; 27192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.exception.VCardException; 28192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.exception.VCardVersionException; 29192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.BluetoothMapBmessage.Status; 30192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.BluetoothMapBmessage.Type; 31192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.utils.BmsgTokenizer; 32192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.utils.BmsgTokenizer.Property; 33192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 34192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.ByteArrayInputStream; 35192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.IOException; 36d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwalimport java.nio.charset.StandardCharsets; 37192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.text.ParseException; 38192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 39192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaclass BluetoothMapBmessageParser { 40192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 41192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static String TAG = "BluetoothMapBmessageParser"; 42fb93d457178a706fc3d20df5bbfcff4ca80a1fc2Sanket Agarwal private final static boolean DBG = false; 43192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 44192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static String CRLF = "\r\n"; 45192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 46192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_BMSG = new Property("BEGIN", "BMSG"); 47192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_BMSG = new Property("END", "BMSG"); 48192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 49192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_VCARD = new Property("BEGIN", "VCARD"); 50192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_VCARD = new Property("END", "VCARD"); 51192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 52192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_BENV = new Property("BEGIN", "BENV"); 53192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_BENV = new Property("END", "BENV"); 54192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 55192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_BBODY = new Property("BEGIN", "BBODY"); 56192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_BBODY = new Property("END", "BBODY"); 57192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 58192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_MSG = new Property("BEGIN", "MSG"); 59192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_MSG = new Property("END", "MSG"); 60192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 61192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static int CRLF_LEN = 2; 62192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 63192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 64192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * length of "container" for 'message' in bmessage-body-content: 65192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * BEGIN:MSG<CRLF> + <CRLF> + END:MSG<CRFL> 66192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 67192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static int MSG_CONTAINER_LEN = 22; 68192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 69192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private BmsgTokenizer mParser; 70192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 71192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final BluetoothMapBmessage mBmsg; 72192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 73192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private BluetoothMapBmessageParser() { 74192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg = new BluetoothMapBmessage(); 75192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 76192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 77192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta static public BluetoothMapBmessage createBmessage(String str) { 78192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta BluetoothMapBmessageParser p = new BluetoothMapBmessageParser(); 79192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 80d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal if (DBG) { 81d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal Log.d(TAG, "actual wired contents: " + str); 82d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal } 83d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal 84192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 85192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.parse(str); 86192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (IOException e) { 87192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.e(TAG, "I/O exception when parsing bMessage", e); 88192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return null; 89192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (ParseException e) { 90192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.e(TAG, "Cannot parse bMessage", e); 91192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return null; 92192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 93192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 94192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return p.mBmsg; 95192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 96192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 97192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private ParseException expected(Property... props) { 98192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta boolean first = true; 99192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta StringBuilder sb = new StringBuilder(); 100192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 101192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta for (Property prop : props) { 102192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!first) { 103192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta sb.append(" or "); 104192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 105192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta sb.append(prop); 106192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta first = false; 107192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 108192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 109192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return new ParseException("Expected: " + sb.toString(), mParser.pos()); 110192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 111192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 112192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private void parse(String str) throws IOException, ParseException { 113192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 114192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 115192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 116192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 117192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-object>::= { "BEGIN:BMSG" <CRLF> <bmessage-property> 118192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * [<bmessage-originator>]* <bmessage-envelope> "END:BMSG" <CRLF> } 119192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 120192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 121192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = new BmsgTokenizer(str + CRLF); 122192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 123192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 124192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(BEGIN_BMSG)) { 125192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(BEGIN_BMSG); 126192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 127192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 128192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseProperties(); 129192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 130192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta while (prop.equals(BEGIN_VCARD)) { 131192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 132192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* <bmessage-originator>::= <vcard> <CRLF> */ 133192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 134192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta StringBuilder vcard = new StringBuilder(); 135192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = extractVcard(vcard); 136192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 137192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry entry = parseVcard(vcard.toString()); 138192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mOriginators.add(entry); 139192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 140192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 141192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(BEGIN_BENV)) { 142192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(BEGIN_BENV); 143192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 144192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 145192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseEnvelope(1); 146192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 147192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_BMSG)) { 148192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_BENV); 149192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 150192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 151192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 152192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * there should be no meaningful data left in stream here so we just 153192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * ignore whatever is left 154192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 155192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 156192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = null; 157192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 158192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 159192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property parseProperties() throws ParseException { 160192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 161192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 162192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 163192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 164192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-property>::=<bmessage-version-property> 165192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-readstatus-property> <bmessage-type-property> 166192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-folder-property> <bmessage-version-property>::="VERSION:" 167192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <common-digit>*"."<common-digit>* <CRLF> 168192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-readstatus-property>::="STATUS:" 'readstatus' <CRLF> 169192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-type-property>::="TYPE:" 'type' <CRLF> 170192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-folder-property>::="FOLDER:" 'foldername' <CRLF> 171192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 172192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 173192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta do { 174192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 175192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 176192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.name.equals("VERSION")) { 177192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgVersion = prop.value; 178192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 179192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("STATUS")) { 180192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta for (Status s : Status.values()) { 181192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.value.equals(s.toString())) { 182192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgStatus = s; 183192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta break; 184192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 185192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 186192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 187192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("TYPE")) { 188192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta for (Type t : Type.values()) { 189192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.value.equals(t.toString())) { 190192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgType = t; 191192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta break; 192192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 193192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 194192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 195192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("FOLDER")) { 196192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgFolder = prop.value; 197192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 198192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 199192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 200192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } while (!prop.equals(BEGIN_VCARD) && !prop.equals(BEGIN_BENV)); 201192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 202192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return prop; 203192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 204192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 205192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property parseEnvelope(int level) throws IOException, ParseException { 206192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 207192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 208192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 209192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 210192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * we can support as many nesting level as we want, but MAP spec clearly 211192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * defines that there should be no more than 3 levels. so we verify it 212192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * here. 213192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 214192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 215192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (level > 3) { 216192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw new ParseException("bEnvelope is nested more than 3 times", mParser.pos()); 217192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 218192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 219192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 220192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-envelope> ::= { "BEGIN:BENV" <CRLF> [<bmessage-recipient>]* 221192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-envelope> | <bmessage-content> "END:BENV" <CRLF> } 222192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 223192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 224192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 225192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 226192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta while (prop.equals(BEGIN_VCARD)) { 227192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 228192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* <bmessage-originator>::= <vcard> <CRLF> */ 229192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 230192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta StringBuilder vcard = new StringBuilder(); 231192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = extractVcard(vcard); 232192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 233192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (level == 1) { 234192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry entry = parseVcard(vcard.toString()); 235192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mRecipients.add(entry); 236192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 237192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 238192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 239192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.equals(BEGIN_BENV)) { 240192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseEnvelope(level + 1); 241192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 242192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.equals(BEGIN_BBODY)) { 243192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseBody(); 244192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 245192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else { 246192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(BEGIN_BENV, BEGIN_BBODY); 247192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 248192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 249192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_BENV)) { 250192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_BENV); 251192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 252192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 253192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mParser.next(); 254192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 255192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 256192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property parseBody() throws IOException, ParseException { 257192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 258192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 259192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 260192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 261192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-content>::= { "BEGIN:BBODY"<CRLF> [<bmessage-body-part-ID> 262192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <CRLF>] <bmessage-body-property> <bmessage-body-content>* <CRLF> 263192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * "END:BBODY"<CRLF> } <bmessage-body-part-ID>::="PARTID:" 'Part-ID' 264192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-property>::=[<bmessage-body-encoding-property>] 265192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * [<bmessage-body-charset-property>] 266192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * [<bmessage-body-language-property>] 267192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-content-length-property> 268192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-encoding-property>::="ENCODING:"'encoding' <CRLF> 269192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-charset-property>::="CHARSET:"'charset' <CRLF> 270192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-language-property>::="LANGUAGE:"'language' <CRLF> 271192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-content-length-property>::= "LENGTH:" <common-digit>* 272192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <CRLF> 273192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 274192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 275192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta do { 276192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 277192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 278192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.name.equals("PARTID")) { 279192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("ENCODING")) { 280192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyEncoding = prop.value; 281192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 282192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("CHARSET")) { 283192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyCharset = prop.value; 284192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 285192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("LANGUAGE")) { 286192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyLanguage = prop.value; 287192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 288192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("LENGTH")) { 289192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 2907b55edea45ee5e5447ef8afaa6b144b698ba6f49Narayan Kamath mBmsg.mBbodyLength = Integer.parseInt(prop.value); 291192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (NumberFormatException e) { 292192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw new ParseException("Invalid LENGTH value", mParser.pos()); 293192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 294192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 295192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 296192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 297192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } while (!prop.equals(BEGIN_MSG)); 298192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 299192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 300d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal * check that the charset is always set to UTF-8. We expect only text transfer (in lieu with 301d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal * the MAPv12 specifying only RFC2822 (text only) for MMS/EMAIL and SMS do not support 302d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal * non-text content. If the charset is not set to UTF-8, it is safe to set the message as 303d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal * empty. We force the getMessage (see BluetoothMasClient) to only call getMessage with 304d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal * UTF-8 as the MCE is not obliged to support native charset. 305d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal */ 306d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal if (!mBmsg.mBbodyCharset.equals("UTF-8")) { 307d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal Log.e(TAG, "The charset was not set to charset UTF-8: " + mBmsg.mBbodyCharset); 308d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal } 309d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal 310d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal /* 311192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-content>::={ "BEGIN:MSG"<CRLF> 'message'<CRLF> 312192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * "END:MSG"<CRLF> } 313192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 314192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 315192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta int messageLen = mBmsg.mBbodyLength - MSG_CONTAINER_LEN; 316192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta int offset = messageLen + CRLF_LEN; 317192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta int restartPos = mParser.pos() + offset; 318192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 319192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 320192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * length is specified in bytes so we need to convert from unicode 321192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * string back to bytes array 322192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 323192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 324192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta String remng = mParser.remaining(); 325192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta byte[] data = remng.getBytes(); 326192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 327192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* restart parsing from after 'message'<CRLF> */ 328192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = new BmsgTokenizer(new String(data, offset, data.length - offset), restartPos); 329192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 330192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(true); 331192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 3324231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta if (prop != null) { 3334231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta if (prop.equals(END_MSG)) { 334d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal if (mBmsg.mBbodyCharset.equals("UTF-8")) { 335d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal mBmsg.mMessage = new String(data, 0, messageLen, StandardCharsets.UTF_8); 336d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal } else { 337d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal mBmsg.mMessage = null; 338d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal } 3394231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta } else { 3404231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta /* Handle possible exception for incorrect LENGTH value 3414231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta * from MSE while parsing GET Message response */ 3424231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta Log.e(TAG, "Prop Invalid: "+ prop.toString()); 3434231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta Log.e(TAG, "Possible Invalid LENGTH value"); 3444231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta throw expected(END_MSG); 3454231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta } 346192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else { 347192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 348192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta data = null; 349192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 350192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 351192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * now we check if bMessage can be parsed if LENGTH is handled as 352192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * number of characters instead of number of bytes 353192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 3544231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta if (offset < 0 || offset > remng.length()) { 3554231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta /* Handle possible exception for incorrect LENGTH value 3564231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta * from MSE while parsing GET Message response */ 3574231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta throw new ParseException("Invalid LENGTH value", mParser.pos()); 3584231be1e03b7c5e563fea1fa7c56abfe2a1e61ddHemant Gupta } 359192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 360192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.w(TAG, "byte LENGTH seems to be invalid, trying with char length"); 361192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 362192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = new BmsgTokenizer(remng.substring(offset)); 363192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 364192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 365192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 366192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_MSG)) { 367192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_MSG); 368192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 369192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 370d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal if (mBmsg.mBbodyCharset.equals("UTF-8")) { 371d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal mBmsg.mMessage = remng.substring(0, messageLen); 372d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal } else { 373d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal mBmsg.mMessage = null; 374d87ff784eb2dfac818df247a153be548b13dfb02Sanket Agarwal } 375192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 376192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 377192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 378192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 379192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_BBODY)) { 380192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_BBODY); 381192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 382192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 383192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mParser.next(); 384192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 385192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 386192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property extractVcard(StringBuilder out) throws IOException, ParseException { 387192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 388192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 389192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta out.append(BEGIN_VCARD).append(CRLF); 390192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 391192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta do { 392192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 393192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta out.append(prop).append(CRLF); 394192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } while (!prop.equals(END_VCARD)); 395192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 396192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mParser.next(); 397192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 398192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 399192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private class VcardHandler implements VCardEntryHandler { 400192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 401192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry vcard; 402192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 403192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 404192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void onStart() { 405192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 406192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 407192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 408192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void onEntryCreated(VCardEntry entry) { 409192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta vcard = entry; 410192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 411192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 412192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 413192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void onEnd() { 414192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 415192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta }; 416192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 417192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private VCardEntry parseVcard(String str) throws IOException, ParseException { 418192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry vcard = null; 419192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 420192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 421192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardParser p = new VCardParser_V21(); 422192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntryConstructor c = new VCardEntryConstructor(); 423192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VcardHandler handler = new VcardHandler(); 424192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta c.addEntryHandler(handler); 425192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.addInterpreter(c); 426192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.parse(new ByteArrayInputStream(str.getBytes())); 427192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 428192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta vcard = handler.vcard; 429192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 430192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardVersionException e1) { 431192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 432192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 433192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardParser p = new VCardParser_V30(); 434192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntryConstructor c = new VCardEntryConstructor(); 435192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VcardHandler handler = new VcardHandler(); 436192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta c.addEntryHandler(handler); 437192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.addInterpreter(c); 438192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.parse(new ByteArrayInputStream(str.getBytes())); 439192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 440192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta vcard = handler.vcard; 441192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 442192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardVersionException e2) { 443192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // will throw below 444192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardException e2) { 445192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // will throw below 446192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 447192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 448192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardException e1) { 449192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // will throw below 450192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 451192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 452192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (vcard == null) { 453192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw new ParseException("Cannot parse vCard object (neither 2.1 nor 3.0?)", 454192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser.pos()); 455192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 456192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 457192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return vcard; 458192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 459192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta} 460