1fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/* 2fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Copyright (C) 2013 Samsung System LSI 3fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Licensed under the Apache License, Version 2.0 (the "License"); 4fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* you may not use this file except in compliance with the License. 5fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* You may obtain a copy of the License at 6fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* 7fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* http://www.apache.org/licenses/LICENSE-2.0 8fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* 9fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Unless required by applicable law or agreed to in writing, software 10fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* distributed under the License is distributed on an "AS IS" BASIS, 11fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* See the License for the specific language governing permissions and 13fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* limitations under the License. 14fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie*/ 15fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xiepackage com.android.bluetooth.map; 16fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 17fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.ByteArrayOutputStream; 1870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulzimport java.io.File; 1970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulzimport java.io.FileInputStream; 2070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulzimport java.io.FileNotFoundException; 2170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulzimport java.io.FileOutputStream; 22fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.IOException; 23fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.InputStream; 24fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.UnsupportedEncodingException; 25fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.util.ArrayList; 26fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 2770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulzimport android.os.Environment; 28fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.telephony.PhoneNumberUtils; 29fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.util.Log; 3070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 31fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport com.android.bluetooth.map.BluetoothMapUtils.TYPE; 32fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 33fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xiepublic abstract class BluetoothMapbMessage { 34fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 35fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie protected static String TAG = "BluetoothMapbMessage"; 36326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde protected static final boolean D = BluetoothMapService.DEBUG; 37326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde protected static final boolean V = BluetoothMapService.VERBOSE; 38326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde 395a60e47497f21f64e6d79420dc4c56c1907df22akschulz private String mVersionString = "VERSION:1.0"; 40fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 41fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static int INVALID_VALUE = -1; 42fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 43326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde protected int mAppParamCharset = BluetoothMapAppParams.INVALID_VALUE_PARAMETER; 44fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 45fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* BMSG attributes */ 46326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String mStatus = null; // READ/UNREAD 47326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde protected TYPE mType = null; // SMS/MMS/EMAIL 48fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 49326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String mFolder = null; 50fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 51fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* BBODY attributes */ 52326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private long mPartId = INVALID_VALUE; 53326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde protected String mEncoding = null; 54326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde protected String mCharset = null; 55326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String mLanguage = null; 56fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 57326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private int mBMsgLength = INVALID_VALUE; 58fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 59326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private ArrayList<vCard> mOriginator = null; 60326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private ArrayList<vCard> mRecipient = null; 61fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 62fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 63fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static class vCard { 64fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* VCARD attributes */ 65326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String mVersion; 66326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String mName = null; 67326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String mFormattedName = null; 68326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String[] mPhoneNumbers = {}; 69326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private String[] mEmailAddresses = {}; 70326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private int mEnvLevel = 0; 715a60e47497f21f64e6d79420dc4c56c1907df22akschulz private String[] mBtUcis = {}; 725a60e47497f21f64e6d79420dc4c56c1907df22akschulz private String[] mBtUids = {}; 73fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 74fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 75fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Construct a version 3.0 vCard 76fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param name Structured 77fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param formattedName Formatted name 78fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param phoneNumbers a String[] of phone numbers 79fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param emailAddresses a String[] of email addresses 80fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param the bmessage envelope level (0 is the top/most outer level) 81fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 82fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public vCard(String name, String formattedName, String[] phoneNumbers, 83fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String[] emailAddresses, int envLevel) { 84326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEnvLevel = envLevel; 85326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mVersion = "3.0"; 86326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mName = name != null ? name : ""; 87326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mFormattedName = formattedName != null ? formattedName : ""; 88fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie setPhoneNumbers(phoneNumbers); 89fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (emailAddresses != null) 90326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEmailAddresses = emailAddresses; 91fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 92fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 93fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 94fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Construct a version 2.1 vCard 95fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param name Structured name 96fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param phoneNumbers a String[] of phone numbers 97fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param emailAddresses a String[] of email addresses 98fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param the bmessage envelope level (0 is the top/most outer level) 99fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 100fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public vCard(String name, String[] phoneNumbers, 101fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String[] emailAddresses, int envLevel) { 102326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEnvLevel = envLevel; 103326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mVersion = "2.1"; 104326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mName = name != null ? name : ""; 105fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie setPhoneNumbers(phoneNumbers); 106fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (emailAddresses != null) 107326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEmailAddresses = emailAddresses; 108fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 109fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 110fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 111fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Construct a version 3.0 vCard 112fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param name Structured name 113fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param formattedName Formatted name 114fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param phoneNumbers a String[] of phone numbers 1155a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param emailAddresses a String[] of email addresses if available, else null 1165a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param btUids a String[] of X-BT-UIDs if available, else null 1175a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param btUcis a String[] of X-BT-UCIs if available, else null 118fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 1195a60e47497f21f64e6d79420dc4c56c1907df22akschulz public vCard(String name, String formattedName, 1205a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] phoneNumbers, 1215a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] emailAddresses, 1225a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] btUids, 1235a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] btUcis) { 124326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mVersion = "3.0"; 125326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mName = (name != null) ? name : ""; 126326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mFormattedName = (formattedName != null) ? formattedName : ""; 127fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie setPhoneNumbers(phoneNumbers); 1285a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (emailAddresses != null) { 129326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEmailAddresses = emailAddresses; 1305a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 1315a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (btUcis != null) { 1325a60e47497f21f64e6d79420dc4c56c1907df22akschulz this.mBtUcis = btUcis; 1335a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 134fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 135fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 136fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 137fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Construct a version 2.1 vCard 138fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param name Structured Name 139fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param phoneNumbers a String[] of phone numbers 140fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param emailAddresses a String[] of email addresses 141fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 142fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public vCard(String name, String[] phoneNumbers, String[] emailAddresses) { 143326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mVersion = "2.1"; 144326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mName = name != null ? name : ""; 145fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie setPhoneNumbers(phoneNumbers); 146fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (emailAddresses != null) 147326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEmailAddresses = emailAddresses; 148fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 149fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 150fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private void setPhoneNumbers(String[] numbers) { 151326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(numbers != null && numbers.length > 0) { 152326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mPhoneNumbers = new String[numbers.length]; 153fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie for(int i = 0, n = numbers.length; i < n; i++){ 154326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde String networkNumber = PhoneNumberUtils.extractNetworkPortion(numbers[i]); 1555a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* extractNetworkPortion can return N if the number is a service 1565a60e47497f21f64e6d79420dc4c56c1907df22akschulz * "number" = a string with the a name in (i.e. "Some-Tele-company" would 1575a60e47497f21f64e6d79420dc4c56c1907df22akschulz * return N because of the N in compaNy) 158326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * Hence we need to check if the number is actually a string with alpha chars. 159326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * */ 1605a60e47497f21f64e6d79420dc4c56c1907df22akschulz String strippedNumber = PhoneNumberUtils.stripSeparators(numbers[i]); 1615a60e47497f21f64e6d79420dc4c56c1907df22akschulz Boolean alpha = false; 1625a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(strippedNumber != null){ 1635a60e47497f21f64e6d79420dc4c56c1907df22akschulz alpha = strippedNumber.matches("[0-9]*[a-zA-Z]+[0-9]*"); 1645a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 165326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(networkNumber != null && networkNumber.length() > 1 && !alpha) { 166326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mPhoneNumbers[i] = networkNumber; 167326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } else { 168326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mPhoneNumbers[i] = numbers[i]; 169326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 170fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 171fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 172fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 173fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 174fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public String getFirstPhoneNumber() { 175326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mPhoneNumbers.length > 0) { 176326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mPhoneNumbers[0]; 177fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else 178326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return null; 179fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 180fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 181fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public int getEnvLevel() { 182326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mEnvLevel; 183326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 184326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde 185326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde public String getName() { 186326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mName; 187326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 188326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde 189326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde public String getFirstEmail() { 190326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mEmailAddresses.length > 0) { 191326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mEmailAddresses[0]; 192326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } else 193326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return null; 194fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 1955a60e47497f21f64e6d79420dc4c56c1907df22akschulz public String getFirstBtUci() { 1965a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mBtUcis.length > 0) { 1975a60e47497f21f64e6d79420dc4c56c1907df22akschulz return mBtUcis[0]; 1985a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else 1995a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 2005a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2015a60e47497f21f64e6d79420dc4c56c1907df22akschulz 2025a60e47497f21f64e6d79420dc4c56c1907df22akschulz public String getFirstBtUid() { 2035a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mBtUids.length > 0) { 2045a60e47497f21f64e6d79420dc4c56c1907df22akschulz return mBtUids[0]; 2055a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else 2065a60e47497f21f64e6d79420dc4c56c1907df22akschulz return null; 2075a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 208fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 209fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void encode(StringBuilder sb) 210fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 211fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("BEGIN:VCARD").append("\r\n"); 212326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("VERSION:").append(mVersion).append("\r\n"); 213326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mVersion.equals("3.0") && mFormattedName != null) 214fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 215326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("FN:").append(mFormattedName).append("\r\n"); 216fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 217326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if (mName != null) 218326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("N:").append(mName).append("\r\n"); 219326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde for(String phoneNumber : mPhoneNumbers) 220fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 221fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("TEL:").append(phoneNumber).append("\r\n"); 222fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 223326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde for(String emailAddress : mEmailAddresses) 224fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 225fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("EMAIL:").append(emailAddress).append("\r\n"); 226fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 2275a60e47497f21f64e6d79420dc4c56c1907df22akschulz for(String btUid : mBtUids) 2285a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 2295a60e47497f21f64e6d79420dc4c56c1907df22akschulz sb.append("X-BT-UID:").append(btUid).append("\r\n"); 2305a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 2315a60e47497f21f64e6d79420dc4c56c1907df22akschulz for(String btUci : mBtUcis) 2325a60e47497f21f64e6d79420dc4c56c1907df22akschulz { 2335a60e47497f21f64e6d79420dc4c56c1907df22akschulz sb.append("X-BT-UCI:").append(btUci).append("\r\n"); 2345a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 235fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("END:VCARD").append("\r\n"); 236fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 237fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 238fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 2395a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Parse a vCard from a BMgsReader, where a line containing "BEGIN:VCARD" 2405a60e47497f21f64e6d79420dc4c56c1907df22akschulz * have just been read. 241fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param reader 2425a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param envLevel 243fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @return 244fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 245fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static vCard parseVcard(BMsgReader reader, int envLevel) { 246fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String formattedName = null; 247fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String name = null; 248fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ArrayList<String> phoneNumbers = null; 249fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ArrayList<String> emailAddresses = null; 2505a60e47497f21f64e6d79420dc4c56c1907df22akschulz ArrayList<String> btUids = null; 2515a60e47497f21f64e6d79420dc4c56c1907df22akschulz ArrayList<String> btUcis = null; 252fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String[] parts; 253fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String line = reader.getLineEnforce(); 254fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 2555a60e47497f21f64e6d79420dc4c56c1907df22akschulz while(!line.contains("END:VCARD")){ 256fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = line.trim(); 257fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.startsWith("N:")){ 258fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie parts = line.split("[^\\\\]:"); // Split on "un-escaped" ':' 259fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(parts.length == 2) { 260fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie name = parts[1]; 261fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else 262fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie name = ""; 263fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 264fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.startsWith("FN:")){ 265fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie parts = line.split("[^\\\\]:"); // Split on "un-escaped" ':' 266fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(parts.length == 2) { 267fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie formattedName = parts[1]; 268fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else 269fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie formattedName = ""; 270fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 271fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.startsWith("TEL:")){ 272fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie parts = line.split("[^\\\\]:"); // Split on "un-escaped" ':' 273fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(parts.length == 2) { 274fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String[] subParts = parts[1].split("[^\\\\];"); 275fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(phoneNumbers == null) 276fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie phoneNumbers = new ArrayList<String>(1); 2775a60e47497f21f64e6d79420dc4c56c1907df22akschulz // only keep actual phone number 2785a60e47497f21f64e6d79420dc4c56c1907df22akschulz phoneNumbers.add(subParts[subParts.length-1]); 279fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else {} 280fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Empty phone number - ignore 281fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 282fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.startsWith("EMAIL:")){ 283fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie parts = line.split("[^\\\\]:"); // Split on "un-escaped" : 284fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(parts.length == 2) { 285fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String[] subParts = parts[1].split("[^\\\\];"); 286fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(emailAddresses == null) 287fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie emailAddresses = new ArrayList<String>(1); 2885a60e47497f21f64e6d79420dc4c56c1907df22akschulz // only keep actual email address 2895a60e47497f21f64e6d79420dc4c56c1907df22akschulz emailAddresses.add(subParts[subParts.length-1]); 290fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else {} 291fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Empty email address entry - ignore 292fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 2935a60e47497f21f64e6d79420dc4c56c1907df22akschulz else if(line.startsWith("X-BT-UCI:")){ 2945a60e47497f21f64e6d79420dc4c56c1907df22akschulz parts = line.split("[^\\\\]:"); // Split on "un-escaped" : 2955a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(parts.length == 2) { 2965a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] subParts = parts[1].split("[^\\\\];"); 2975a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(btUcis == null) 2985a60e47497f21f64e6d79420dc4c56c1907df22akschulz btUcis = new ArrayList<String>(1); 2995a60e47497f21f64e6d79420dc4c56c1907df22akschulz btUcis.add(subParts[subParts.length-1]); // only keep actual UCI 3005a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else {} 3015a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Empty UCIentry - ignore 3025a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3035a60e47497f21f64e6d79420dc4c56c1907df22akschulz else if(line.startsWith("X-BT-UID:")){ 3045a60e47497f21f64e6d79420dc4c56c1907df22akschulz parts = line.split("[^\\\\]:"); // Split on "un-escaped" : 3055a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(parts.length == 2) { 3065a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] subParts = parts[1].split("[^\\\\];"); 3075a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(btUids == null) 3085a60e47497f21f64e6d79420dc4c56c1907df22akschulz btUids = new ArrayList<String>(1); 3095a60e47497f21f64e6d79420dc4c56c1907df22akschulz btUids.add(subParts[subParts.length-1]); // only keep actual UID 3105a60e47497f21f64e6d79420dc4c56c1907df22akschulz } else {} 3115a60e47497f21f64e6d79420dc4c56c1907df22akschulz // Empty UID entry - ignore 3125a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 3135a60e47497f21f64e6d79420dc4c56c1907df22akschulz 3145a60e47497f21f64e6d79420dc4c56c1907df22akschulz 315fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 316fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 317fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return new vCard(name, formattedName, 3185a60e47497f21f64e6d79420dc4c56c1907df22akschulz phoneNumbers == null? 3195a60e47497f21f64e6d79420dc4c56c1907df22akschulz null : phoneNumbers.toArray(new String[phoneNumbers.size()]), 3205a60e47497f21f64e6d79420dc4c56c1907df22akschulz emailAddresses == null ? 3215a60e47497f21f64e6d79420dc4c56c1907df22akschulz null : emailAddresses.toArray(new String[emailAddresses.size()]), 322fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie envLevel); 323fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 324fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie }; 325fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 326fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private static class BMsgReader { 327fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie InputStream mInStream; 328fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public BMsgReader(InputStream is) 329fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 330fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie this.mInStream = is; 331fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 332fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 333fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private byte[] getLineAsBytes() { 334fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int readByte; 335fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 336fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* TODO: Actually the vCard spec. allows to break lines by using a newLine 3375a60e47497f21f64e6d79420dc4c56c1907df22akschulz * followed by a white space character(space or tab). Not sure this is a good idea to 3385a60e47497f21f64e6d79420dc4c56c1907df22akschulz * implement as the Bluetooth MAP spec. illustrates vCards using tab alignment, 3395a60e47497f21f64e6d79420dc4c56c1907df22akschulz * hence actually showing an invalid vCard format... 340fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * If we read such a folded line, the folded part will be skipped in the parser 341326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * UPDATE: Check if we actually do unfold before parsing the input stream 342fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 343fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 344fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ByteArrayOutputStream output = new ByteArrayOutputStream(); 345fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 346fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while ((readByte = mInStream.read()) != -1) { 347fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (readByte == '\r') { 348fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if ((readByte = mInStream.read()) != -1 && readByte == '\n') { 349fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(output.size() == 0) 350fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie continue; /* Skip empty lines */ 351fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else 352fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie break; 353fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 354fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie output.write('\r'); 355fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 356fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else if (readByte == '\n' && output.size() == 0) { 357fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* Empty line - skip */ 358fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie continue; 359fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 360fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 361fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie output.write(readByte); 362fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 363fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 364fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.w(TAG, e); 365fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return null; 366fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 367fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return output.toByteArray(); 368fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 369fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 370fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 371fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Read a line of text from the BMessage. 372fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @return the next line of text, or null at end of file, or if UTF-8 is not supported. 373fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 374fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public String getLine() { 375fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 376fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie byte[] line = getLineAsBytes(); 377fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (line.length == 0) 378fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return null; 379fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else 380fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return new String(line, "UTF-8"); 381fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (UnsupportedEncodingException e) { 382fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.w(TAG, e); 383fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return null; 384fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 385fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 386fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 387fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 388fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * same as getLine(), but throws an exception, if we run out of lines. 389fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Use this function when ever more lines are needed for the bMessage to be complete. 390fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @return the next line 391fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 392fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public String getLineEnforce() { 39370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz String line = getLine(); 39470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if (line == null) 39570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz throw new IllegalArgumentException("Bmessage too short"); 39670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 39770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz return line; 398fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 399fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 400fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 401fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 402fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Reads a line from the InputStream, and examines if the subString 403fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * matches the line read. 404fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param subString 405fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * The string to match against the line. 406fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @throws IllegalArgumentException 407fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * If the expected substring is not found. 408fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * 409fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 410fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void expect(String subString) throws IllegalArgumentException{ 411fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String line = getLine(); 41270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(line == null || subString == null){ 41370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz throw new IllegalArgumentException("Line or substring is null"); 41470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz }else if(!line.toUpperCase().contains(subString.toUpperCase())) 4155a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Expected \"" + subString + "\" in: \"" 4165a60e47497f21f64e6d79420dc4c56c1907df22akschulz + line + "\""); 417fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 418fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 419fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 420fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Same as expect(String), but with two strings. 421fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param subString 422fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param subString2 423fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @throws IllegalArgumentException 424fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * If one or all of the strings are not found. 425fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 426fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void expect(String subString, String subString2) throws IllegalArgumentException{ 427fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String line = getLine(); 42870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(!line.toUpperCase().contains(subString.toUpperCase())) 4295a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Expected \"" + subString + "\" in: \"" 4305a60e47497f21f64e6d79420dc4c56c1907df22akschulz + line + "\""); 43170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(!line.toUpperCase().contains(subString2.toUpperCase())) 4325a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Expected \"" + subString + "\" in: \"" 4335a60e47497f21f64e6d79420dc4c56c1907df22akschulz + line + "\""); 434fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 435fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 436fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 437fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Read a part of the bMessage as raw data. 438fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param length the number of bytes to read 4395a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @return the byte[] containing the number of bytes or null if an error occurs or EOF is 4405a60e47497f21f64e6d79420dc4c56c1907df22akschulz * reached before length bytes have been read. 441fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 442fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public byte[] getDataBytes(int length) { 443fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie byte[] data = new byte[length]; 444fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 445fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int bytesRead; 446fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int offset=0; 4475a60e47497f21f64e6d79420dc4c56c1907df22akschulz while ((bytesRead = mInStream.read(data, offset, length-offset)) 4485a60e47497f21f64e6d79420dc4c56c1907df22akschulz != (length - offset)) { 449fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(bytesRead == -1) 450fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return null; 451fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie offset += bytesRead; 452fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 453fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 454fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.w(TAG, e); 455fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return null; 456fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 457fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return data; 458fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 459fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie }; 460fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 46170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz public BluetoothMapbMessage(){ 462fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 463fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 464fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 4655a60e47497f21f64e6d79420dc4c56c1907df22akschulz public String getVersionString() { 4665a60e47497f21f64e6d79420dc4c56c1907df22akschulz return mVersionString; 4675a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 4685a60e47497f21f64e6d79420dc4c56c1907df22akschulz /** 4695a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Set the version string for VCARD 4705a60e47497f21f64e6d79420dc4c56c1907df22akschulz * @param version the actual number part of the version string i.e. 1.0 4715a60e47497f21f64e6d79420dc4c56c1907df22akschulz * */ 4725a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void setVersionString(String version) { 4735a60e47497f21f64e6d79420dc4c56c1907df22akschulz this.mVersionString = "VERSION:"+version; 4745a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 4755a60e47497f21f64e6d79420dc4c56c1907df22akschulz 4765a60e47497f21f64e6d79420dc4c56c1907df22akschulz public static BluetoothMapbMessage parse(InputStream bMsgStream, 4775a60e47497f21f64e6d79420dc4c56c1907df22akschulz int appParamCharset) throws IllegalArgumentException{ 47870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz BMsgReader reader; 479fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String line = ""; 480fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothMapbMessage newBMsg = null; 481fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie boolean status = false; 482fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie boolean statusFound = false; 483fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie TYPE type = null; 484fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String folder = null; 485fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 4865a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* This section is used for debug. It will write the incoming message to a file on the 4875a60e47497f21f64e6d79420dc4c56c1907df22akschulz * SD-card, hence should only be used for test/debug. 48870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz * If an error occurs, it will result in a OBEX_HTTP_PRECON_FAILED to be send to the client, 4895a60e47497f21f64e6d79420dc4c56c1907df22akschulz * even though the message might be formatted correctly, hence only enable this code for 4905a60e47497f21f64e6d79420dc4c56c1907df22akschulz * test. */ 49170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(V) { 49270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz /* Read the entire stream into a file on the SD card*/ 49370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz File sdCard = Environment.getExternalStorageDirectory(); 49470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz File dir = new File (sdCard.getAbsolutePath() + "/bluetooth/log/"); 49570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz dir.mkdirs(); 49670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz File file = new File(dir, "receivedBMessage.txt"); 49770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz FileOutputStream outStream = null; 49870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz boolean failed = false; 49970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz int writtenLen = 0; 50070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 50170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz try { 5025a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* overwrite if it does already exist */ 5035a60e47497f21f64e6d79420dc4c56c1907df22akschulz outStream = new FileOutputStream(file, false); 50470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 50570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz byte[] buffer = new byte[4*1024]; 50670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz int len = 0; 50770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz while ((len = bMsgStream.read(buffer)) > 0) { 50870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz outStream.write(buffer, 0, len); 50970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz writtenLen += len; 51070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 51170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } catch (FileNotFoundException e) { 51270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz Log.e(TAG,"Unable to create output stream",e); 51370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } catch (IOException e) { 51470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz Log.e(TAG,"Failed to copy the received message",e); 51570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(writtenLen != 0) 5165a60e47497f21f64e6d79420dc4c56c1907df22akschulz failed = true; /* We failed to write the complete file, 5175a60e47497f21f64e6d79420dc4c56c1907df22akschulz hence the received message is lost... */ 51870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } finally { 51970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(outStream != null) 52070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz try { 52170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz outStream.close(); 52270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } catch (IOException e) { 52370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 52470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 52570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 52670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz /* Return if we corrupted the incoming bMessage. */ 52770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(failed) { 52870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz throw new IllegalArgumentException(); /* terminate this function with an error. */ 52970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 53070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 53170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if (outStream == null) { 5325a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* We failed to create the log-file, just continue using the original bMsgStream. */ 53370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } else { 53470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz /* overwrite the bMsgStream using the file written to the SD-Card */ 53570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz try { 53670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz bMsgStream.close(); 53770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } catch (IOException e) { 53870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz /* Ignore if we cannot close the stream. */ 53970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 54070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz /* Open the file and overwrite bMsgStream to read from the file */ 54170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz try { 54270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz bMsgStream = new FileInputStream(file); 54370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } catch (FileNotFoundException e) { 54470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz Log.e(TAG,"Failed to open the bMessage file", e); 5455a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* terminate this function with an error */ 5465a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException(); 54770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 54870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 54970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz Log.i(TAG, "The incoming bMessage have been dumped to " + file.getAbsolutePath()); 55070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } /* End of if(V) log-section */ 55170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 55270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz reader = new BMsgReader(bMsgStream); 55370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz reader.expect("BEGIN:BMSG"); 5545a60e47497f21f64e6d79420dc4c56c1907df22akschulz reader.expect("VERSION"); 55570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 556fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 557fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Parse the properties - which end with either a VCARD or a BENV 558fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while(!line.contains("BEGIN:VCARD") && !line.contains("BEGIN:BENV")) { 559fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("STATUS")){ 560fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 561fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 562fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg[1].trim().equals("READ")) { 563fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie status = true; 564fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else if (arg[1].trim().equals("UNREAD")) { 565fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie status =false; 566fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 567fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Wrong value in 'STATUS': " + arg[1]); 568fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 569fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 570fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'STATUS': " + line); 571fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 572fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 5735a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(line.contains("EXTENDEDDATA")){ 5745a60e47497f21f64e6d79420dc4c56c1907df22akschulz String arg[] = line.split(":"); 5755a60e47497f21f64e6d79420dc4c56c1907df22akschulz if (arg != null && arg.length == 2) { 5765a60e47497f21f64e6d79420dc4c56c1907df22akschulz String value = arg[1].trim(); 5775a60e47497f21f64e6d79420dc4c56c1907df22akschulz //FIXME what should we do with this 5785a60e47497f21f64e6d79420dc4c56c1907df22akschulz Log.i(TAG,"We got extended data with: "+value); 5795a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 5805a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 581fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("TYPE")) { 582fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 583fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 584fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String value = arg[1].trim(); 5855a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* Will throw IllegalArgumentException if value is wrong */ 5865a60e47497f21f64e6d79420dc4c56c1907df22akschulz type = TYPE.valueOf(value); 587fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(appParamCharset == BluetoothMapAppParams.CHARSET_NATIVE 588fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie && type != TYPE.SMS_CDMA && type != TYPE.SMS_GSM) { 5895a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Native appParamsCharset " 5905a60e47497f21f64e6d79420dc4c56c1907df22akschulz +"only supported for SMS"); 591fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 592fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie switch(type) { 593fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie case SMS_CDMA: 594fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie case SMS_GSM: 595fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie newBMsg = new BluetoothMapbMessageSms(); 596fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie break; 597fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie case MMS: 5985a60e47497f21f64e6d79420dc4c56c1907df22akschulz newBMsg = new BluetoothMapbMessageMime(); 599326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde break; 600fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie case EMAIL: 601326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde newBMsg = new BluetoothMapbMessageEmail(); 602fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie break; 6035a60e47497f21f64e6d79420dc4c56c1907df22akschulz case IM: 6045a60e47497f21f64e6d79420dc4c56c1907df22akschulz newBMsg = new BluetoothMapbMessageMime(); 6055a60e47497f21f64e6d79420dc4c56c1907df22akschulz break; 606fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie default: 607fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie break; 608fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 609fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 610fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'TYPE':" + line); 611fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 612fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 613fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("FOLDER")) { 614fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String[] arg = line.split(":"); 615fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 616fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie folder = arg[1].trim(); 617fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 61870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz // This can be empty for push message - hence ignore if there is no value 619fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 620fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 621fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 622fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(newBMsg == null) 6235a60e47497f21f64e6d79420dc4c56c1907df22akschulz throw new IllegalArgumentException("Missing bMessage TYPE: "+ 6245a60e47497f21f64e6d79420dc4c56c1907df22akschulz "- unable to parse body-content"); 625fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie newBMsg.setType(type); 626326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde newBMsg.mAppParamCharset = appParamCharset; 627fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(folder != null) 62870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz newBMsg.setCompleteFolder(folder); 629fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(statusFound) 630fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie newBMsg.setStatus(status); 631fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 632fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Now check for originator VCARDs 633fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while(line.contains("BEGIN:VCARD")){ 634fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(D) Log.d(TAG,"Decoding vCard"); 635fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie newBMsg.addOriginator(vCard.parseVcard(reader,0)); 636fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 637fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 638fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("BEGIN:BENV")) { 639fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie newBMsg.parseEnvelope(reader, 0); 640fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else 641fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Bmessage has no BEGIN:BENV - line:" + line); 642fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 6435a60e47497f21f64e6d79420dc4c56c1907df22akschulz /* TODO: Do we need to validate the END:* tags? They are only needed if someone puts 6445a60e47497f21f64e6d79420dc4c56c1907df22akschulz * additional info below the END:MSG - in which case we don't handle it. 6455a60e47497f21f64e6d79420dc4c56c1907df22akschulz * We need to parse the message based on the length field, to ensure MAP 1.0 6465a60e47497f21f64e6d79420dc4c56c1907df22akschulz * compatibility, since this spec. do not suggest to escape the end-tag if it 6475a60e47497f21f64e6d79420dc4c56c1907df22akschulz * occurs inside the message text. 648fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 64970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 65070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz try { 65170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz bMsgStream.close(); 65270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } catch (IOException e) { 65370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz /* Ignore if we cannot close the stream. */ 65470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 65570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 656fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return newBMsg; 657fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 658fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 659fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private void parseEnvelope(BMsgReader reader, int level) { 660fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String line; 661fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 662fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(D) Log.d(TAG,"Decoding envelope level " + level); 663fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 664fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while(line.contains("BEGIN:VCARD")){ 665fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(D) Log.d(TAG,"Decoding recipient vCard level " + level); 666326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mRecipient == null) 667326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRecipient = new ArrayList<vCard>(1); 668326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRecipient.add(vCard.parseVcard(reader, level)); 669fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 670fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 671fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("BEGIN:BENV")) { 672fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(D) Log.d(TAG,"Decoding nested envelope"); 673fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie parseEnvelope(reader, ++level); // Nested BENV 674fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 675fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("BEGIN:BBODY")){ 676fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(D) Log.d(TAG,"Decoding bbody"); 677fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie parseBody(reader); 678fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 679fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 680fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 681fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private void parseBody(BMsgReader reader) { 682fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String line; 683fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 684da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker parseMsgInit(); 685fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while(!line.contains("END:")) { 686fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(line.contains("PARTID:")) { 687fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 688fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 689fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 690326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mPartId = Long.parseLong(arg[1].trim()); 691fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (NumberFormatException e) { 692fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Wrong value in 'PARTID': " + arg[1]); 693fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 694fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 695fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'PARTID': " + line); 696fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 697fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 698fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.contains("ENCODING:")) { 699fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 700fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 701326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mEncoding = arg[1].trim(); 702326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde // If needed validation will be done when the value is used 703fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 704fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'ENCODING': " + line); 705fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 706fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 707fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.contains("CHARSET:")) { 708fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 709fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 710326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mCharset = arg[1].trim(); 711326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde // If needed validation will be done when the value is used 712fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 713fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'CHARSET': " + line); 714fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 715fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 716fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.contains("LANGUAGE:")) { 717fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 718fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 719326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mLanguage = arg[1].trim(); 720326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde // If needed validation will be done when the value is used 721fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 722fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'LANGUAGE': " + line); 723fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 724fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 725fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.contains("LENGTH:")) { 726fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String arg[] = line.split(":"); 727fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (arg != null && arg.length == 2) { 728fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 729326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mBMsgLength = Integer.parseInt(arg[1].trim()); 730fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (NumberFormatException e) { 731fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Wrong value in 'LENGTH': " + arg[1]); 732fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 733fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 734fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new IllegalArgumentException("Missing value for 'LENGTH': " + line); 735fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 736fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 737fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else if(line.contains("BEGIN:MSG")) { 738da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker if (V) Log.v(TAG, "bMsgLength: " + mBMsgLength); 739326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mBMsgLength == INVALID_VALUE) 740326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde throw new IllegalArgumentException("Missing value for 'LENGTH'. " + 741326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde "Unable to read remaining part of the message"); 742da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker 743326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde /* For SMS: Encoding of MSG is always UTF-8 compliant, regardless of any properties, 744326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde since PDUs are encodes as hex-strings */ 745fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* PTS has a bug regarding the message length, and sets it 2 bytes too short, hence 746fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * using the length field to determine the amount of data to read, might not be the 747fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * best solution. 748da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker * Errata ESR06 section 5.8.12 introduced escaping of END:MSG in the actual message 749da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker * content, it is now safe to use the END:MSG tag as terminator, and simply ignore 750da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker * the length field.*/ 751da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker 752da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker // Read until we receive END:MSG as some carkits send bad message lengths 753da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker String data = ""; 754da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker String message_line = ""; 755da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker while (!message_line.equals("END:MSG")) { 756da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker data += message_line; 757da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker message_line = reader.getLineEnforce(); 758fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 759da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker 760da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker // The MAP spec says that all END:MSG strings in the body 761da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker // of the message must be escaped upon encoding and the 762da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker // escape removed upon decoding 763da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker data.replaceAll("([/]*)/END\\:MSG", "$1END:MSG"); 764da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker data.trim(); 765da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker 766da6bccfffb75e8452faafa8906082dc192cd7ce9Ajay Panicker parseMsgPart(data); 767fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 768fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie line = reader.getLineEnforce(); 769fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 770fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 771fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 772fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 773fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Parse the 'message' part of <bmessage-body-content>" 774fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param msgPart 775fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 776fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public abstract void parseMsgPart(String msgPart); 777fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 778fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Set initial values before parsing - will be called is a message body is found 779fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * during parsing. 780fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 781fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public abstract void parseMsgInit(); 782fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 783fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public abstract byte[] encode() throws UnsupportedEncodingException; 784fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 785fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void setStatus(boolean read) { 786fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(read) 787326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mStatus = "READ"; 788fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie else 789326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mStatus = "UNREAD"; 790fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 791fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 792fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void setType(TYPE type) { 793326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mType = type; 794fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 795fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 796fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 797fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @return the type 798fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 799fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public TYPE getType() { 800326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mType; 801fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 802fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 80370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz public void setCompleteFolder(String folder) { 804326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mFolder = folder; 80570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 80670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 807fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void setFolder(String folder) { 808326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mFolder = "telecom/msg/" + folder; 809fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 810fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 81170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz public String getFolder() { 812326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mFolder; 81370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 81470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 81570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 816fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void setEncoding(String encoding) { 817326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mEncoding = encoding; 818fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 819fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 820fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public ArrayList<vCard> getOriginators() { 821326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mOriginator; 822fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 823fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 824fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void addOriginator(vCard originator) { 825326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(this.mOriginator == null) 826326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mOriginator = new ArrayList<vCard>(); 827326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mOriginator.add(originator); 828fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 829fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 830fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 831fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Add a version 3.0 vCard with a formatted name 832fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param name e.g. Bonde;Casper 833fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param formattedName e.g. "Casper Bonde" 834fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param phoneNumbers 835fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param emailAddresses 836fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 8375a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void addOriginator(String name, String formattedName, 8385a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] phoneNumbers, 8395a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] emailAddresses, 8405a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] btUids, 8415a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] btUcis) { 842326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mOriginator == null) 843326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mOriginator = new ArrayList<vCard>(); 8445a60e47497f21f64e6d79420dc4c56c1907df22akschulz mOriginator.add(new vCard(name, formattedName, phoneNumbers, 8455a60e47497f21f64e6d79420dc4c56c1907df22akschulz emailAddresses, btUids, btUcis)); 846fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 847fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 8485a60e47497f21f64e6d79420dc4c56c1907df22akschulz 8495a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void addOriginator(String[] btUcis, String[] btUids) { 8505a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mOriginator == null) 8515a60e47497f21f64e6d79420dc4c56c1907df22akschulz mOriginator = new ArrayList<vCard>(); 8525a60e47497f21f64e6d79420dc4c56c1907df22akschulz mOriginator.add(new vCard(null,null,null,null,btUids, btUcis)); 8535a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 8545a60e47497f21f64e6d79420dc4c56c1907df22akschulz 8555a60e47497f21f64e6d79420dc4c56c1907df22akschulz 856fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** Add a version 2.1 vCard with only a name. 857fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * 858fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param name e.g. Bonde;Casper 859fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param phoneNumbers 860fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param emailAddresses 861fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 862fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void addOriginator(String name, String[] phoneNumbers, String[] emailAddresses) { 863326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mOriginator == null) 864326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mOriginator = new ArrayList<vCard>(); 865326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mOriginator.add(new vCard(name, phoneNumbers, emailAddresses)); 866fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 867fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 868fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public ArrayList<vCard> getRecipients() { 869326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde return mRecipient; 870fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 871fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 872fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void setRecipient(vCard recipient) { 873326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(this.mRecipient == null) 874326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mRecipient = new ArrayList<vCard>(); 875326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde this.mRecipient.add(recipient); 876fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 8775a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void addRecipient(String[] btUcis, String[] btUids) { 8785a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(mRecipient == null) 8795a60e47497f21f64e6d79420dc4c56c1907df22akschulz mRecipient = new ArrayList<vCard>(); 8805a60e47497f21f64e6d79420dc4c56c1907df22akschulz mRecipient.add(new vCard(null,null,null,null,btUids, btUcis)); 8815a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 8825a60e47497f21f64e6d79420dc4c56c1907df22akschulz public void addRecipient(String name, String formattedName, 8835a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] phoneNumbers, 8845a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] emailAddresses, 8855a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] btUids, 8865a60e47497f21f64e6d79420dc4c56c1907df22akschulz String[] btUcis) { 887326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mRecipient == null) 888326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRecipient = new ArrayList<vCard>(); 8895a60e47497f21f64e6d79420dc4c56c1907df22akschulz mRecipient.add(new vCard(name, formattedName, phoneNumbers, 8905a60e47497f21f64e6d79420dc4c56c1907df22akschulz emailAddresses,btUids, btUcis)); 891fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 892fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 893fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void addRecipient(String name, String[] phoneNumbers, String[] emailAddresses) { 894326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mRecipient == null) 895326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRecipient = new ArrayList<vCard>(); 896326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRecipient.add(new vCard(name, phoneNumbers, emailAddresses)); 897fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 898fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 899fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 9005a60e47497f21f64e6d79420dc4c56c1907df22akschulz * Convert a byte[] of data to a hex string representation, converting each nibble to the 9015a60e47497f21f64e6d79420dc4c56c1907df22akschulz * corresponding hex char. 9025a60e47497f21f64e6d79420dc4c56c1907df22akschulz * NOTE: There is not need to escape instances of "\r\nEND:MSG" in the binary data represented 9035a60e47497f21f64e6d79420dc4c56c1907df22akschulz * as a string as only the characters [0-9] and [a-f] is used. 904fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param pduData the byte-array of data. 905fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param scAddressData the byte-array of the encoded sc-Address. 906fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @return the resulting string. 907fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 908fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie protected String encodeBinary(byte[] pduData, byte[] scAddressData) { 909fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie StringBuilder out = new StringBuilder((pduData.length + scAddressData.length)*2); 910fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie for(int i = 0; i < scAddressData.length; i++) { 911fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie out.append(Integer.toString((scAddressData[i] >> 4) & 0x0f,16)); // MS-nibble first 912fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie out.append(Integer.toString( scAddressData[i] & 0x0f,16)); 913fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 914fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie for(int i = 0; i < pduData.length; i++) { 915fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie out.append(Integer.toString((pduData[i] >> 4) & 0x0f,16)); // MS-nibble first 916fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie out.append(Integer.toString( pduData[i] & 0x0f,16)); 9175a60e47497f21f64e6d79420dc4c56c1907df22akschulz /*out.append(Integer.toHexString(data[i]));*/ /* This is the same as above, but does not 9185a60e47497f21f64e6d79420dc4c56c1907df22akschulz * include the needed 0's 9195a60e47497f21f64e6d79420dc4c56c1907df22akschulz * e.g. it converts the value 3 to "3" 9205a60e47497f21f64e6d79420dc4c56c1907df22akschulz * and not "03" */ 921fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 922fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return out.toString(); 923fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 924fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 925fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /** 926fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * Decodes a binary hex-string encoded UTF-8 string to the represented binary data set. 927fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @param data The string representation of the data - must have an even number of characters. 928fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * @return the byte[] represented in the data. 929fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 930fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie protected byte[] decodeBinary(String data) { 931fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie byte[] out = new byte[data.length()/2]; 932fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie String value; 933fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(D) Log.d(TAG,"Decoding binary data: START:" + data + ":END"); 934fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie for(int i = 0, j = 0, n = out.length; i < n; i++) 935fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 9365a60e47497f21f64e6d79420dc4c56c1907df22akschulz value = data.substring(j++, ++j); 9375a60e47497f21f64e6d79420dc4c56c1907df22akschulz // same as data.substring(2*i, 2*i+1+1) - substring() uses end-1 for last index 93870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz out[i] = (byte)(Integer.valueOf(value, 16) & 0xff); 93970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 94070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(D) { 94170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz StringBuilder sb = new StringBuilder(out.length); 94270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz for(int i = 0, n = out.length; i < n; i++) 94370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz { 94470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz sb.append(String.format("%02X",out[i] & 0xff)); 94570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz } 94670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz Log.d(TAG,"Decoded binary data: START:" + sb.toString() + ":END"); 947fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 948fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return out; 949fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 950fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 951fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public byte[] encodeGeneric(ArrayList<byte[]> bodyFragments) throws UnsupportedEncodingException 952fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie { 953fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie StringBuilder sb = new StringBuilder(256); 954fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie byte[] msgStart, msgEnd; 955fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("BEGIN:BMSG").append("\r\n"); 9565a60e47497f21f64e6d79420dc4c56c1907df22akschulz 9575a60e47497f21f64e6d79420dc4c56c1907df22akschulz sb.append(mVersionString).append("\r\n"); 958326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("STATUS:").append(mStatus).append("\r\n"); 959326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("TYPE:").append(mType.name()).append("\r\n"); 960326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mFolder.length() > 512) 9615a60e47497f21f64e6d79420dc4c56c1907df22akschulz sb.append("FOLDER:").append( 9625a60e47497f21f64e6d79420dc4c56c1907df22akschulz mFolder.substring(mFolder.length()-512, mFolder.length())).append("\r\n"); 96370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz else 964326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("FOLDER:").append(mFolder).append("\r\n"); 9655a60e47497f21f64e6d79420dc4c56c1907df22akschulz if(!mVersionString.contains("1.0")){ 9665a60e47497f21f64e6d79420dc4c56c1907df22akschulz sb.append("EXTENDEDDATA:").append("\r\n"); 9675a60e47497f21f64e6d79420dc4c56c1907df22akschulz } 968326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mOriginator != null){ 969326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde for(vCard element : mOriginator) 970fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie element.encode(sb); 971fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 972326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde /* If we need the three levels of env. at some point - we do have a level in the 973326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * vCards that could be used to determine the levels of the envelope. 974fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 975fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 976fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("BEGIN:BENV").append("\r\n"); 977326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mRecipient != null){ 978326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde for(vCard element : mRecipient) { 979326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(V) Log.v(TAG, "encodeGeneric: recipient email" + element.getFirstEmail()); 980fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie element.encode(sb); 981326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 982fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 983fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("BEGIN:BBODY").append("\r\n"); 984326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mEncoding != null && mEncoding != "") 985326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("ENCODING:").append(mEncoding).append("\r\n"); 986326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mCharset != null && mCharset != "") 987326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sb.append("CHARSET:").append(mCharset).append("\r\n"); 988fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 989fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 990fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int length = 0; 991fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* 22 is the length of the 'BEGIN:MSG' and 'END:MSG' + 3*CRLF */ 992fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie for (byte[] fragment : bodyFragments) { 993fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie length += fragment.length + 22; 994fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 995fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("LENGTH:").append(length).append("\r\n"); 996fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 997fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Extract the initial part of the bMessage string 998fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie msgStart = sb.toString().getBytes("UTF-8"); 999fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 1000fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb = new StringBuilder(31); 1001fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("END:BBODY").append("\r\n"); 1002fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("END:BENV").append("\r\n"); 1003fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie sb.append("END:BMSG").append("\r\n"); 1004fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 1005fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie msgEnd = sb.toString().getBytes("UTF-8"); 1006fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 1007fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 1008fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 10095a60e47497f21f64e6d79420dc4c56c1907df22akschulz ByteArrayOutputStream stream = new ByteArrayOutputStream( 10105a60e47497f21f64e6d79420dc4c56c1907df22akschulz msgStart.length + msgEnd.length + length); 1011fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie stream.write(msgStart); 1012fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 1013fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie for (byte[] fragment : bodyFragments) { 1014fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie stream.write("BEGIN:MSG\r\n".getBytes("UTF-8")); 1015fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie stream.write(fragment); 1016fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie stream.write("\r\nEND:MSG\r\n".getBytes("UTF-8")); 1017fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 1018fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie stream.write(msgEnd); 1019fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 1020fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(V) Log.v(TAG,stream.toString("UTF-8")); 1021fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return stream.toByteArray(); 1022fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 1023fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.w(TAG,e); 1024fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return null; 1025fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 1026fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 1027fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie} 1028