BluetoothMapbMessage.java revision 326b5e610063ac24c0ba467ac585bd4c7f618a67
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
39fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static final String VERSION = "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;
71fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
72fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
73fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Construct a version 3.0 vCard
74fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param name Structured
75fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param formattedName Formatted name
76fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param phoneNumbers a String[] of phone numbers
77fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param emailAddresses a String[] of email addresses
78fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param the bmessage envelope level (0 is the top/most outer level)
79fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
80fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public vCard(String name, String formattedName, String[] phoneNumbers,
81fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String[] emailAddresses, int envLevel) {
82326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mEnvLevel = envLevel;
83326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mVersion = "3.0";
84326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mName = name != null ? name : "";
85326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mFormattedName = formattedName != null ? formattedName : "";
86fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            setPhoneNumbers(phoneNumbers);
87fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (emailAddresses != null)
88326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                this.mEmailAddresses = emailAddresses;
89fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
90fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
91fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
92fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Construct a version 2.1 vCard
93fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param name Structured name
94fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param phoneNumbers a String[] of phone numbers
95fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param emailAddresses a String[] of email addresses
96fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param the bmessage envelope level (0 is the top/most outer level)
97fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
98fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public vCard(String name, String[] phoneNumbers,
99fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String[] emailAddresses, int envLevel) {
100326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mEnvLevel = envLevel;
101326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mVersion = "2.1";
102326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mName = name != null ? name : "";
103fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            setPhoneNumbers(phoneNumbers);
104fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (emailAddresses != null)
105326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                this.mEmailAddresses = emailAddresses;
106fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
107fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
108fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
109fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Construct a version 3.0 vCard
110fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param name Structured name
111fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param formattedName Formatted name
112fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param phoneNumbers a String[] of phone numbers
113fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param emailAddresses a String[] of email addresses
114fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
115fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public vCard(String name, String formattedName, String[] phoneNumbers, String[] emailAddresses) {
116326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mVersion = "3.0";
117326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mName = (name != null) ? name : "";
118326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mFormattedName = (formattedName != null) ? formattedName : "";
119fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            setPhoneNumbers(phoneNumbers);
120fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (emailAddresses != null)
121326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                this.mEmailAddresses = emailAddresses;
122fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
123fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
124fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
125fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Construct a version 2.1 vCard
126fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param name Structured Name
127fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param phoneNumbers a String[] of phone numbers
128fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param emailAddresses a String[] of email addresses
129fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
130fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public vCard(String name, String[] phoneNumbers, String[] emailAddresses) {
131326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mVersion = "2.1";
132326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mName = name != null ? name : "";
133fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            setPhoneNumbers(phoneNumbers);
134fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if (emailAddresses != null)
135326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                this.mEmailAddresses = emailAddresses;
136fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
137fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
138fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private void setPhoneNumbers(String[] numbers) {
139326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(numbers != null && numbers.length > 0) {
140326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mPhoneNumbers = new String[numbers.length];
141fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                for(int i = 0, n = numbers.length; i < n; i++){
142326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    String networkNumber = PhoneNumberUtils.extractNetworkPortion(numbers[i]);
143326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    /* extractNetworkPortion can return N if the number is a service "number" = a string
144326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                     * with the a name in (i.e. "Some-Tele-company" would return N because of the N in compaNy)
145326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                     * Hence we need to check if the number is actually a string with alpha chars.
146326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                     * */
147326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    Boolean alpha = PhoneNumberUtils.stripSeparators(numbers[i]).matches("[0-9]*[a-zA-Z]+[0-9]*");
148326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    if(networkNumber != null && networkNumber.length() > 1 && !alpha) {
149326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        mPhoneNumbers[i] = networkNumber;
150326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    } else {
151326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        mPhoneNumbers[i] = numbers[i];
152326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    }
153fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
154fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
155fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
156fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
157fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public String getFirstPhoneNumber() {
158326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mPhoneNumbers.length > 0) {
159326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                return mPhoneNumbers[0];
160fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } else
161326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                return null;
162fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
163fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
164fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public int getEnvLevel() {
165326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mEnvLevel;
166326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        }
167326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde
168326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        public String getName() {
169326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            return mName;
170326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        }
171326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde
172326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        public String getFirstEmail() {
173326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mEmailAddresses.length > 0) {
174326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                return mEmailAddresses[0];
175326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            } else
176326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                return null;
177fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
178fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
179fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void encode(StringBuilder sb)
180fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        {
181fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            sb.append("BEGIN:VCARD").append("\r\n");
182326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            sb.append("VERSION:").append(mVersion).append("\r\n");
183326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mVersion.equals("3.0") && mFormattedName != null)
184fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            {
185326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                sb.append("FN:").append(mFormattedName).append("\r\n");
186fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
187326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if (mName != null)
188326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                sb.append("N:").append(mName).append("\r\n");
189326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            for(String phoneNumber : mPhoneNumbers)
190fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            {
191fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                sb.append("TEL:").append(phoneNumber).append("\r\n");
192fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
193326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            for(String emailAddress : mEmailAddresses)
194fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            {
195fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                sb.append("EMAIL:").append(emailAddress).append("\r\n");
196fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
197fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            sb.append("END:VCARD").append("\r\n");
198fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
199fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
200fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
201fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Parse a vCard from a BMgsReader, where a line containing "BEGIN:VCARD" have just been read.
202fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param reader
203326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde         * @param mOriginator
204fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @return
205fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
206fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public static vCard parseVcard(BMsgReader reader, int envLevel) {
207fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String formattedName = null;
208fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String name = null;
209fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ArrayList<String> phoneNumbers = null;
210fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ArrayList<String> emailAddresses = null;
211fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String[] parts;
212fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String line = reader.getLineEnforce();
213fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
214fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            while(!line.contains("END:VCARD")) {
215fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                line = line.trim();
216fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if(line.startsWith("N:")){
217fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    parts = line.split("[^\\\\]:"); // Split on "un-escaped" ':'
218fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(parts.length == 2) {
219fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        name = parts[1];
220fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else
221fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        name = "";
222fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
223fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                else if(line.startsWith("FN:")){
224fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    parts = line.split("[^\\\\]:"); // Split on "un-escaped" ':'
225fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(parts.length == 2) {
226fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        formattedName = parts[1];
227fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else
228fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        formattedName = "";
229fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
230fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                else if(line.startsWith("TEL:")){
231fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    parts = line.split("[^\\\\]:"); // Split on "un-escaped" ':'
232fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(parts.length == 2) {
233fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        String[] subParts = parts[1].split("[^\\\\];");
234fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        if(phoneNumbers == null)
235fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            phoneNumbers = new ArrayList<String>(1);
236fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        phoneNumbers.add(subParts[subParts.length-1]); // only keep actual phone number
237fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else {}
238fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        // Empty phone number - ignore
239fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
240fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                else if(line.startsWith("EMAIL:")){
241fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    parts = line.split("[^\\\\]:"); // Split on "un-escaped" :
242fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(parts.length == 2) {
243fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        String[] subParts = parts[1].split("[^\\\\];");
244fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        if(emailAddresses == null)
245fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            emailAddresses = new ArrayList<String>(1);
246fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        emailAddresses.add(subParts[subParts.length-1]); // only keep actual email address
247fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else {}
248fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        // Empty email address entry - ignore
249fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
250fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                line = reader.getLineEnforce();
251fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
252fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return new vCard(name, formattedName,
253fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    phoneNumbers == null? null : phoneNumbers.toArray(new String[phoneNumbers.size()]),
254fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    emailAddresses == null ? null : emailAddresses.toArray(new String[emailAddresses.size()]),
255fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    envLevel);
256fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
257fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    };
258fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
259fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private static class BMsgReader {
260fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        InputStream mInStream;
261fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public BMsgReader(InputStream is)
262fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        {
263fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            this.mInStream = is;
264fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
265fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
266fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        private byte[] getLineAsBytes() {
267fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            int readByte;
268fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
269fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            /* TODO: Actually the vCard spec. allows to break lines by using a newLine
270fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             * followed by a white space character(space or tab). Not sure this is a good idea to implement
271fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             * as the Bluetooth MAP spec. illustrates vCards using tab alignment, hence actually
272fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             * showing an invalid vCard format...
273fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             * If we read such a folded line, the folded part will be skipped in the parser
274326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde             * UPDATE: Check if we actually do unfold before parsing the input stream
275fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie             */
276fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
277fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ByteArrayOutputStream output = new ByteArrayOutputStream();
278fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            try {
279fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                while ((readByte = mInStream.read()) != -1) {
280fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if (readByte == '\r') {
281fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        if ((readByte = mInStream.read()) != -1 && readByte == '\n') {
282fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            if(output.size() == 0)
283fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                                continue; /* Skip empty lines */
284fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            else
285fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                                break;
286fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        } else {
287fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            output.write('\r');
288fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        }
289fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else if (readByte == '\n' && output.size() == 0) {
290fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        /* Empty line - skip */
291fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        continue;
292fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
293fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
294fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    output.write(readByte);
295fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
296fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } catch (IOException e) {
297fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.w(TAG, e);
298fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return null;
299fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
300fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return output.toByteArray();
301fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
302fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
303fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
304fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Read a line of text from the BMessage.
305fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @return the next line of text, or null at end of file, or if UTF-8 is not supported.
306fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
307fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public String getLine() {
308fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            try {
309fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                byte[] line = getLineAsBytes();
310fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (line.length == 0)
311fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return null;
312fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                else
313fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    return new String(line, "UTF-8");
314fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } catch (UnsupportedEncodingException e) {
315fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.w(TAG, e);
316fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return null;
317fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
318fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
319fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
320fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
321fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * same as getLine(), but throws an exception, if we run out of lines.
322fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Use this function when ever more lines are needed for the bMessage to be complete.
323fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @return the next line
324fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
325fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public String getLineEnforce() {
32670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        String line = getLine();
32770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        if (line == null)
32870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            throw new IllegalArgumentException("Bmessage too short");
32970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
33070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        return line;
331fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
332fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
333fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
334fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
335fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Reads a line from the InputStream, and examines if the subString
336fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * matches the line read.
337fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param subString
338fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * The string to match against the line.
339fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @throws IllegalArgumentException
340fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * If the expected substring is not found.
341fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *
342fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
343fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void expect(String subString) throws IllegalArgumentException{
344fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String line = getLine();
34570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(line == null || subString == null){
34670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                throw new IllegalArgumentException("Line or substring is null");
34770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }else if(!line.toUpperCase().contains(subString.toUpperCase()))
348fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                throw new IllegalArgumentException("Expected \"" + subString + "\" in: \"" + line + "\"");
349fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
350fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
351fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
352fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Same as expect(String), but with two strings.
353fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param subString
354fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param subString2
355fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @throws IllegalArgumentException
356fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * If one or all of the strings are not found.
357fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
358fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public void expect(String subString, String subString2) throws IllegalArgumentException{
359fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            String line = getLine();
36070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(!line.toUpperCase().contains(subString.toUpperCase()))
361fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                throw new IllegalArgumentException("Expected \"" + subString + "\" in: \"" + line + "\"");
36270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(!line.toUpperCase().contains(subString2.toUpperCase()))
363fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                throw new IllegalArgumentException("Expected \"" + subString + "\" in: \"" + line + "\"");
364fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
365fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
366fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /**
367fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * Read a part of the bMessage as raw data.
368fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @param length the number of bytes to read
369fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * @return the byte[] containing the number of bytes or null if an error occurs or EOF is reached
370fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         * before length bytes have been read.
371fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
372fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        public byte[] getDataBytes(int length) {
373fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            byte[] data = new byte[length];
374fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            try {
375fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                int bytesRead;
376fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                int offset=0;
37770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                while ((bytesRead = mInStream.read(data, offset, length-offset)) != (length - offset)) {
378fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(bytesRead == -1)
379fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        return null;
380fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    offset += bytesRead;
381fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
382fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            } catch (IOException e) {
383fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                Log.w(TAG, e);
384fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                return null;
385fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
386fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return data;
387fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
388fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    };
389fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
39070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz    public BluetoothMapbMessage(){
391fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
392fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
393fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
394fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public static BluetoothMapbMessage parse(InputStream bMsgStream, int appParamCharset) throws IllegalArgumentException{
39570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        BMsgReader reader;
396fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String line = "";
397fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        BluetoothMapbMessage newBMsg = null;
398fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        boolean status = false;
399fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        boolean statusFound = false;
400fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        TYPE type = null;
401fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String folder = null;
402fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
40370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        /* This section is used for debug. It will write the incoming message to a file on the SD-card,
40470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz         * hence should only be used for test/debug.
40570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz         * If an error occurs, it will result in a OBEX_HTTP_PRECON_FAILED to be send to the client,
40670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz         * even though the message might be formatted correctly, hence only enable this code for test. */
40770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        if(V) {
40870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            /* Read the entire stream into a file on the SD card*/
40970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            File sdCard = Environment.getExternalStorageDirectory();
41070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            File dir = new File (sdCard.getAbsolutePath() + "/bluetooth/log/");
41170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            dir.mkdirs();
41270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            File file = new File(dir, "receivedBMessage.txt");
41370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            FileOutputStream outStream = null;
41470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            boolean failed = false;
41570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            int writtenLen = 0;
41670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
41770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            try {
41870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                outStream = new FileOutputStream(file, false); /* overwrite if it does already exist */
41970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
42070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                byte[] buffer = new byte[4*1024];
42170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                int len = 0;
42270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                while ((len = bMsgStream.read(buffer)) > 0) {
42370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    outStream.write(buffer, 0, len);
42470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    writtenLen += len;
42570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                }
42670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            } catch (FileNotFoundException e) {
42770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                Log.e(TAG,"Unable to create output stream",e);
42870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            } catch (IOException e) {
42970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                Log.e(TAG,"Failed to copy the received message",e);
43070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                if(writtenLen != 0)
43170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    failed = true; /* We failed to write the complete file, hence the received message is lost... */
43270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            } finally {
43370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                if(outStream != null)
43470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    try {
43570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                        outStream.close();
43670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    } catch (IOException e) {
43770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    }
43870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }
43970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
44070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            /* Return if we corrupted the incoming bMessage. */
44170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if(failed) {
44270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                throw new IllegalArgumentException(); /* terminate this function with an error. */
44370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }
44470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
44570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            if (outStream == null) {
44670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                /* We failed to create the the log-file, just continue using the original bMsgStream. */
44770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            } else {
44870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                /* overwrite the bMsgStream using the file written to the SD-Card */
44970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                try {
45070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    bMsgStream.close();
45170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                } catch (IOException e) {
45270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    /* Ignore if we cannot close the stream. */
45370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                }
45470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                /* Open the file and overwrite bMsgStream to read from the file */
45570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                try {
45670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    bMsgStream = new FileInputStream(file);
45770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                } catch (FileNotFoundException e) {
45870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    Log.e(TAG,"Failed to open the bMessage file", e);
45970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    throw new IllegalArgumentException(); /* terminate this function with an error. */
46070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                }
46170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }
46270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            Log.i(TAG, "The incoming bMessage have been dumped to " + file.getAbsolutePath());
46370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        } /* End of if(V) log-section */
46470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
46570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        reader = new BMsgReader(bMsgStream);
46670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        reader.expect("BEGIN:BMSG");
46770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        reader.expect("VERSION","1.0");
46870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
469fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        line = reader.getLineEnforce();
470fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        // Parse the properties - which end with either a VCARD or a BENV
471fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        while(!line.contains("BEGIN:VCARD") && !line.contains("BEGIN:BENV")) {
472fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(line.contains("STATUS")){
473fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
474fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
475fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if (arg[1].trim().equals("READ")) {
476fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        status = true;
477fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else if (arg[1].trim().equals("UNREAD")) {
478fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        status =false;
479fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } else {
480fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        throw new IllegalArgumentException("Wrong value in 'STATUS': " + arg[1]);
481fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
482fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
483fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'STATUS': " + line);
484fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
485fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
486fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(line.contains("TYPE")) {
487fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
488fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
489fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    String value = arg[1].trim();
490fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    type = TYPE.valueOf(value); // Will throw IllegalArgumentException if value is wrong
491fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(appParamCharset == BluetoothMapAppParams.CHARSET_NATIVE
492fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                            && type != TYPE.SMS_CDMA && type != TYPE.SMS_GSM) {
493fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        throw new IllegalArgumentException("Native appParamsCharset only supported for SMS");
494fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
495fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    switch(type) {
496fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    case SMS_CDMA:
497fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    case SMS_GSM:
498fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        newBMsg = new BluetoothMapbMessageSms();
499fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        break;
500fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    case MMS:
501326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        newBMsg = new BluetoothMapbMessageMms();
502326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        break;
503fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    case EMAIL:
504326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        newBMsg = new BluetoothMapbMessageEmail();
505fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        break;
506fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    default:
507fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        break;
508fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
509fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
510fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'TYPE':" + line);
511fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
512fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
513fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(line.contains("FOLDER")) {
514fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String[] arg = line.split(":");
515fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
516fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    folder = arg[1].trim();
517fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
51870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                // This can be empty for push message - hence ignore if there is no value
519fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
520fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            line = reader.getLineEnforce();
521fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
522fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(newBMsg == null)
523fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            throw new IllegalArgumentException("Missing bMessage TYPE: - unable to parse body-content");
524fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        newBMsg.setType(type);
525326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        newBMsg.mAppParamCharset = appParamCharset;
526fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(folder != null)
52770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            newBMsg.setCompleteFolder(folder);
528fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(statusFound)
529fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            newBMsg.setStatus(status);
530fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
531fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        // Now check for originator VCARDs
532fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        while(line.contains("BEGIN:VCARD")){
533fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(D) Log.d(TAG,"Decoding vCard");
534fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            newBMsg.addOriginator(vCard.parseVcard(reader,0));
535fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            line = reader.getLineEnforce();
536fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
537fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(line.contains("BEGIN:BENV")) {
538fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            newBMsg.parseEnvelope(reader, 0);
539fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } else
540fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            throw new IllegalArgumentException("Bmessage has no BEGIN:BENV - line:" + line);
541fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
542fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* TODO: Do we need to validate the END:* tags? They are only needed if someone puts additional info
543fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         *        below the END:MSG - in which case we don't handle it.
544326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde         *        We need to parse the message based on the length field, to ensure MAP 1.0 compatibility,
545326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde         *        since this spec. do not suggest to escape the end-tag if it occurs inside the message text.
546fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
54770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
54870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        try {
54970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            bMsgStream.close();
55070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        } catch (IOException e) {
55170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            /* Ignore if we cannot close the stream. */
55270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        }
55370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
554fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return newBMsg;
555fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
556fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
557fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private void parseEnvelope(BMsgReader reader, int level) {
558fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String line;
559fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        line = reader.getLineEnforce();
560fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(D) Log.d(TAG,"Decoding envelope level " + level);
561fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
562fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie       while(line.contains("BEGIN:VCARD")){
563fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie           if(D) Log.d(TAG,"Decoding recipient vCard level " + level);
564326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            if(mRecipient == null)
565326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                mRecipient = new ArrayList<vCard>(1);
566326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mRecipient.add(vCard.parseVcard(reader, level));
567fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            line = reader.getLineEnforce();
568fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
569fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(line.contains("BEGIN:BENV")) {
570fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(D) Log.d(TAG,"Decoding nested envelope");
571fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            parseEnvelope(reader, ++level); // Nested BENV
572fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
573fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(line.contains("BEGIN:BBODY")){
574fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(D) Log.d(TAG,"Decoding bbody");
575fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            parseBody(reader);
576fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
577fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
578fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
579fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    private void parseBody(BMsgReader reader) {
580fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String line;
581fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        line = reader.getLineEnforce();
582fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        while(!line.contains("END:")) {
583fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(line.contains("PARTID:")) {
584fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
585fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
586fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    try {
587326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mPartId = Long.parseLong(arg[1].trim());
588fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } catch (NumberFormatException e) {
589fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        throw new IllegalArgumentException("Wrong value in 'PARTID': " + arg[1]);
590fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
591fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
592fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'PARTID': " + line);
593fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
594fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
595fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else if(line.contains("ENCODING:")) {
596fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
597fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
598326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mEncoding = arg[1].trim();
599326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    // If needed validation will be done when the value is used
600fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
601fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'ENCODING': " + line);
602fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
603fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
604fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else if(line.contains("CHARSET:")) {
605fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
606fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
607326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mCharset = arg[1].trim();
608326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    // If needed validation will be done when the value is used
609fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
610fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'CHARSET': " + line);
611fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
612fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
613fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else if(line.contains("LANGUAGE:")) {
614fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
615fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
616326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    mLanguage = arg[1].trim();
617326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    // If needed validation will be done when the value is used
618fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
619fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'LANGUAGE': " + line);
620fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
621fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
622fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else if(line.contains("LENGTH:")) {
623fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String arg[] = line.split(":");
624fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                if (arg != null && arg.length == 2) {
625fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    try {
626326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        mBMsgLength = Integer.parseInt(arg[1].trim());
627fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    } catch (NumberFormatException e) {
628fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        throw new IllegalArgumentException("Wrong value in 'LENGTH': " + arg[1]);
629fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
630fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } else {
631fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Missing value for 'LENGTH': " + line);
632fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
633fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
634fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            else if(line.contains("BEGIN:MSG")) {
635326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                if(mBMsgLength == INVALID_VALUE)
636326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                    throw new IllegalArgumentException("Missing value for 'LENGTH'. " +
637326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                            "Unable to read remaining part of the message");
638326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                /* For SMS: Encoding of MSG is always UTF-8 compliant, regardless of any properties,
639326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                   since PDUs are encodes as hex-strings */
640fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                /* PTS has a bug regarding the message length, and sets it 2 bytes too short, hence
641fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * using the length field to determine the amount of data to read, might not be the
642fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * best solution.
643fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * Since errata ???(bluetooth.org is down at the moment) introduced escaping of END:MSG
644fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * in the actual message content, it is now safe to use the END:MSG tag as terminator,
645fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * and simply ignore the length field.*/
646326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                byte[] rawData = reader.getDataBytes(mBMsgLength - (line.getBytes().length + 2)); // 2 added to compensate for the removed \r\n
647fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String data;
648fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                try {
649fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    data = new String(rawData, "UTF-8");
650fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    if(V) {
651326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                        Log.v(TAG,"MsgLength: " + mBMsgLength);
652fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        Log.v(TAG,"line.getBytes().length: " + line.getBytes().length);
653fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        String debug = line.replaceAll("\\n", "<LF>\n");
654fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        debug = debug.replaceAll("\\r", "<CR>");
655fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        Log.v(TAG,"The line: \"" + debug + "\"");
656fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        debug = data.replaceAll("\\n", "<LF>\n");
657fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        debug = debug.replaceAll("\\r", "<CR>");
658fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                        Log.v(TAG,"The msgString: \"" + debug + "\"");
659fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    }
660fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                } catch (UnsupportedEncodingException e) {
661fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    Log.w(TAG,e);
662fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    throw new IllegalArgumentException("Unable to convert to UTF-8");
663fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
664fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                /* Decoding of MSG:
665fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * 1) split on "\r\nEND:MSG\r\n"
666fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * 2) delete "BEGIN:MSG\r\n" for each msg
667fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * 3) replace any occurrence of "\END:MSG" with "END:MSG"
668fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * 4) based on charset from application properties either store as String[] or decode to raw PDUs
669fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                 * */
670fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                String messages[] = data.split("\r\nEND:MSG\r\n");
671fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                parseMsgInit();
672fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                for(int i = 0; i < messages.length; i++) {
67370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                    messages[i] = messages[i].replaceFirst("^BEGIN:MSG\r\n", "");
674fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    messages[i] = messages[i].replaceAll("\r\n([/]*)/END\\:MSG", "\r\n$1END:MSG");
675fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    messages[i] = messages[i].trim();
676fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                    parseMsgPart(messages[i]);
677fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                }
678fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
679fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            line = reader.getLineEnforce();
680fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
681fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
682fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
683fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
684fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Parse the 'message' part of <bmessage-body-content>"
685fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param msgPart
686fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
687fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public abstract void parseMsgPart(String msgPart);
688fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
689fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Set initial values before parsing - will be called is a message body is found
690fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * during parsing.
691fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
692fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public abstract void parseMsgInit();
693fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
694fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public abstract byte[] encode() throws UnsupportedEncodingException;
695fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
696fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void setStatus(boolean read) {
697fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(read)
698326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mStatus = "READ";
699fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        else
700326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mStatus = "UNREAD";
701fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
702fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
703fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void setType(TYPE type) {
704326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        this.mType = type;
705fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
706fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
707fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
708fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @return the type
709fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
710fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public TYPE getType() {
711326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        return mType;
712fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
713fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
71470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz    public void setCompleteFolder(String folder) {
715326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        this.mFolder = folder;
71670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz    }
71770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
718fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void setFolder(String folder) {
719326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        this.mFolder = "telecom/msg/" + folder;
720fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
721fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
72270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz    public String getFolder() {
723326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        return mFolder;
72470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz    }
72570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
72670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz
727fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void setEncoding(String encoding) {
728326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        this.mEncoding = encoding;
729fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
730fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
731fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public ArrayList<vCard> getOriginators() {
732326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        return mOriginator;
733fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
734fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
735fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void addOriginator(vCard originator) {
736326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(this.mOriginator == null)
737326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mOriginator = new ArrayList<vCard>();
738326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        this.mOriginator.add(originator);
739fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
740fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
741fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
742fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Add a version 3.0 vCard with a formatted name
743fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param name e.g. Bonde;Casper
744fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param formattedName e.g. "Casper Bonde"
745fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param phoneNumbers
746fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param emailAddresses
747fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
748fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void addOriginator(String name, String formattedName, String[] phoneNumbers, String[] emailAddresses) {
749326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mOriginator == null)
750326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mOriginator = new ArrayList<vCard>();
751326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        mOriginator.add(new vCard(name, formattedName, phoneNumbers, emailAddresses));
752fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
753fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
754fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /** Add a version 2.1 vCard with only a name.
755fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     *
756fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param name e.g. Bonde;Casper
757fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param phoneNumbers
758fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param emailAddresses
759fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
760fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void addOriginator(String name, String[] phoneNumbers, String[] emailAddresses) {
761326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mOriginator == null)
762326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mOriginator = new ArrayList<vCard>();
763326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        mOriginator.add(new vCard(name, phoneNumbers, emailAddresses));
764fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
765fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
766fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public ArrayList<vCard> getRecipients() {
767326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        return mRecipient;
768fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
769fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
770fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void setRecipient(vCard recipient) {
771326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(this.mRecipient == null)
772326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            this.mRecipient = new ArrayList<vCard>();
773326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        this.mRecipient.add(recipient);
774fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
775fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
776fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void addRecipient(String name, String formattedName, String[] phoneNumbers, String[] emailAddresses) {
777326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mRecipient == null)
778326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mRecipient = new ArrayList<vCard>();
779326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        mRecipient.add(new vCard(name, formattedName, phoneNumbers, emailAddresses));
780fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
781fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
782fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public void addRecipient(String name, String[] phoneNumbers, String[] emailAddresses) {
783326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mRecipient == null)
784326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            mRecipient = new ArrayList<vCard>();
785326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        mRecipient.add(new vCard(name, phoneNumbers, emailAddresses));
786fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
787fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
788fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
789fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Convert a byte[] of data to a hex string representation, converting each nibble to the corresponding
790fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * hex char.
791fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * NOTE: There is not need to escape instances of "\r\nEND:MSG" in the binary data represented as a string
792fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     *       as only the characters [0-9] and [a-f] is used.
793fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param pduData the byte-array of data.
794fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param scAddressData the byte-array of the encoded sc-Address.
795fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @return the resulting string.
796fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
797fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    protected String encodeBinary(byte[] pduData, byte[] scAddressData) {
798fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        StringBuilder out = new StringBuilder((pduData.length + scAddressData.length)*2);
799fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        for(int i = 0; i < scAddressData.length; i++) {
800fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            out.append(Integer.toString((scAddressData[i] >> 4) & 0x0f,16)); // MS-nibble first
801fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            out.append(Integer.toString( scAddressData[i]       & 0x0f,16));
802fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
803fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        for(int i = 0; i < pduData.length; i++) {
804fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            out.append(Integer.toString((pduData[i] >> 4) & 0x0f,16)); // MS-nibble first
805fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            out.append(Integer.toString( pduData[i]       & 0x0f,16));
806fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            /*out.append(Integer.toHexString(data[i]));*/ /* This is the same as above, but does not include the needed 0's
807fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                                                           e.g. it converts the value 3 to "3" and not "03" */
808fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
809fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return out.toString();
810fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
811fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
812fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    /**
813fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * Decodes a binary hex-string encoded UTF-8 string to the represented binary data set.
814fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @param data The string representation of the data - must have an even number of characters.
815fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     * @return the byte[] represented in the data.
816fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie     */
817fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    protected byte[] decodeBinary(String data) {
818fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        byte[] out = new byte[data.length()/2];
819fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        String value;
820fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        if(D) Log.d(TAG,"Decoding binary data: START:" + data + ":END");
821fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        for(int i = 0, j = 0, n = out.length; i < n; i++)
822fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        {
82370be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            value = data.substring(j++, ++j); // same as data.substring(2*i, 2*i+1+1) - substring() uses end-1 for last index
82470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            out[i] = (byte)(Integer.valueOf(value, 16) & 0xff);
82570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        }
82670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        if(D) {
82770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            StringBuilder sb = new StringBuilder(out.length);
82870be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            for(int i = 0, n = out.length; i < n; i++)
82970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            {
83070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz                sb.append(String.format("%02X",out[i] & 0xff));
83170be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            }
83270be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz            Log.d(TAG,"Decoded binary data: START:" + sb.toString() + ":END");
833fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
834fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        return out;
835fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
836fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
837fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    public byte[] encodeGeneric(ArrayList<byte[]> bodyFragments) throws UnsupportedEncodingException
838fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    {
839fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        StringBuilder sb = new StringBuilder(256);
840fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        byte[] msgStart, msgEnd;
841fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("BEGIN:BMSG").append("\r\n");
842fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append(VERSION).append("\r\n");
843326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        sb.append("STATUS:").append(mStatus).append("\r\n");
844326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        sb.append("TYPE:").append(mType.name()).append("\r\n");
845326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mFolder.length() > 512)
846326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            sb.append("FOLDER:").append(mFolder.substring(mFolder.length()-512, mFolder.length())).append("\r\n");
84770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz        else
848326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            sb.append("FOLDER:").append(mFolder).append("\r\n");
849326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mOriginator != null){
850326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            for(vCard element : mOriginator)
851fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                element.encode(sb);
852fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
853326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        /* If we need the three levels of env. at some point - we do have a level in the
854326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde         *  vCards that could be used to determine the levels of the envelope.
855fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie         */
856fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
857fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("BEGIN:BENV").append("\r\n");
858326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mRecipient != null){
859326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            for(vCard element : mRecipient) {
860326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde                if(V) Log.v(TAG, "encodeGeneric: recipient email" + element.getFirstEmail());
861fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                element.encode(sb);
862326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            }
863fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
864fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("BEGIN:BBODY").append("\r\n");
865326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mEncoding != null && mEncoding != "")
866326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            sb.append("ENCODING:").append(mEncoding).append("\r\n");
867326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde        if(mCharset != null && mCharset != "")
868326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde            sb.append("CHARSET:").append(mCharset).append("\r\n");
869fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
870fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
871fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        int length = 0;
872fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        /* 22 is the length of the 'BEGIN:MSG' and 'END:MSG' + 3*CRLF */
873fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        for (byte[] fragment : bodyFragments) {
874fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            length += fragment.length + 22;
875fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
876fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("LENGTH:").append(length).append("\r\n");
877fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
878fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        // Extract the initial part of the bMessage string
879fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        msgStart = sb.toString().getBytes("UTF-8");
880fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
881fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb = new StringBuilder(31);
882fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("END:BBODY").append("\r\n");
883fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("END:BENV").append("\r\n");
884fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        sb.append("END:BMSG").append("\r\n");
885fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
886fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        msgEnd = sb.toString().getBytes("UTF-8");
887fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
888fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        try {
889fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
890fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            ByteArrayOutputStream stream = new ByteArrayOutputStream(msgStart.length + msgEnd.length + length);
891fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            stream.write(msgStart);
892fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
893fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            for (byte[] fragment : bodyFragments) {
894fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                stream.write("BEGIN:MSG\r\n".getBytes("UTF-8"));
895fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                stream.write(fragment);
896fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie                stream.write("\r\nEND:MSG\r\n".getBytes("UTF-8"));
897fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            }
898fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            stream.write(msgEnd);
899fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie
900fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            if(V) Log.v(TAG,stream.toString("UTF-8"));
901fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return stream.toByteArray();
902fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        } catch (IOException e) {
903fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            Log.w(TAG,e);
904fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie            return null;
905fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie        }
906fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie    }
907fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie}
908