1/*
2* Copyright (C) 2013 Samsung System LSI
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7*      http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15package com.android.bluetooth.map;
16
17import java.io.UnsupportedEncodingException;
18import java.nio.ByteBuffer;
19import java.nio.ByteOrder;
20import java.text.ParseException;
21import java.text.SimpleDateFormat;
22import java.util.Arrays;
23import java.util.Date;
24import java.util.UUID;
25
26import android.util.Log;
27
28import com.android.bluetooth.SignedLongLong;
29
30/**
31 * This class encapsulates the appParams needed for MAP.
32 */
33public class BluetoothMapAppParams {
34
35    private static final String TAG = "BluetoothMapAppParams";
36
37    private static final int MAX_LIST_COUNT           = 0x01;
38    private static final int START_OFFSET             = 0x02;
39    private static final int FILTER_MESSAGE_TYPE      = 0x03;
40    private static final int FILTER_PERIOD_BEGIN      = 0x04;
41    private static final int FILTER_PERIOD_END        = 0x05;
42    private static final int FILTER_READ_STATUS       = 0x06;
43    private static final int FILTER_RECIPIENT         = 0x07;
44    private static final int FILTER_ORIGINATOR        = 0x08;
45    private static final int FILTER_PRIORITY          = 0x09;
46    private static final int ATTACHMENT               = 0x0A;
47    private static final int TRANSPARENT              = 0x0B;
48    private static final int RETRY                    = 0x0C;
49    private static final int NEW_MESSAGE              = 0x0D;
50    private static final int NOTIFICATION_STATUS      = 0x0E;
51    private static final int MAS_INSTANCE_ID          = 0x0F;
52    private static final int PARAMETER_MASK           = 0x10;
53    private static final int FOLDER_LISTING_SIZE      = 0x11;
54    private static final int MESSAGE_LISTING_SIZE     = 0x12;
55    private static final int SUBJECT_LENGTH           = 0x13;
56    private static final int CHARSET                  = 0x14;
57    private static final int FRACTION_REQUEST         = 0x15;
58    private static final int FRACTION_DELIVER         = 0x16;
59    private static final int STATUS_INDICATOR         = 0x17;
60    private static final int STATUS_VALUE             = 0x18;
61    private static final int MSE_TIME                 = 0x19;
62    private static final int DATABASE_INDETIFIER      = 0x1A;
63    private static final int CONVO_LIST_VER_COUNTER   = 0x1B;
64    private static final int PRESENCE_AVAILABLE       = 0x1C;
65    private static final int PRESENCE_TEXT            = 0x1D;
66    private static final int LAST_ACTIVITY            = 0x1E;
67    private static final int CHAT_STATE               = 0x1F;
68    private static final int FILTER_CONVO_ID          = 0x20;
69    private static final int CONVO_LISTING_SIZE       = 0x21;
70    private static final int FILTER_PRESENCE          = 0x22;
71    private static final int FILTER_UID_PRESENT       = 0x23;
72    private static final int CHAT_STATE_CONVO_ID      = 0x24;
73    private static final int FOLDER_VER_COUNTER       = 0x25;
74    private static final int FILTER_MESSAGE_HANDLE    = 0x26;
75    private static final int NOTIFICATION_FILTER      = 0x27;
76    private static final int CONVO_PARAMETER_MASK     = 0x28;
77
78    // Length defined for Application Parameters
79    private static final int MAX_LIST_COUNT_LEN       = 0x02; //, 0x0000, 0xFFFF),
80    private static final int START_OFFSET_LEN         = 0x02; //, 0x0000, 0xFFFF),
81    private static final int FILTER_MESSAGE_TYPE_LEN  = 0x01; //, 0x0000, 0x000f),
82    private static final int FILTER_READ_STATUS_LEN   = 0x01; //, 0x0000, 0x0002),
83    private static final int FILTER_PRIORITY_LEN      = 0x01; //, 0x0000, 0x0002),
84    private static final int ATTACHMENT_LEN           = 0x01; //, 0x0000, 0x0001),
85    private static final int TRANSPARENT_LEN          = 0x01; //, 0x0000, 0x0001),
86    private static final int RETRY_LEN                = 0x01; //, 0x0000, 0x0001),
87    private static final int NEW_MESSAGE_LEN          = 0x01; //, 0x0000, 0x0001),
88    private static final int NOTIFICATION_STATUS_LEN  = 0x01; //, 0x0000, 0xFFFF),
89    private static final int MAS_INSTANCE_ID_LEN      = 0x01; //, 0x0000, 0x00FF),
90    private static final int PARAMETER_MASK_LEN       = 0x04; //, 0x0000, 0x0000),
91    private static final int FOLDER_LISTING_SIZE_LEN  = 0x02; //, 0x0000, 0xFFFF),
92    private static final int MESSAGE_LISTING_SIZE_LEN = 0x02; //, 0x0000, 0xFFFF),
93    private static final int SUBJECT_LENGTH_LEN       = 0x01; //, 0x0000, 0x00FF),
94    private static final int CHARSET_LEN              = 0x01; //, 0x0000, 0x0001),
95    private static final int FRACTION_REQUEST_LEN     = 0x01; //, 0x0000, 0x0001),
96    private static final int FRACTION_DELIVER_LEN     = 0x01; //, 0x0000, 0x0001),
97    private static final int STATUS_INDICATOR_LEN     = 0x01; //, 0x0000, 0x0001),
98    private static final int STATUS_VALUE_LEN         = 0x01; //, 0x0000, 0x0001),
99    private static final int DATABASE_INDETIFIER_LEN  = 0x10;
100    private static final int CONVO_LIST_VER_COUNTER_LEN = 0x10;
101    private static final int PRESENCE_AVAILABLE_LEN   = 0X01;
102    private static final int CHAT_STATE_LEN           = 0x01;
103    private static final int CHAT_STATE_CONVO_ID_LEN  = 0x10;
104    private static final int FILTER_CONVO_ID_LEN      = 0x20;
105    private static final int CONVO_LISTING_SIZE_LEN   = 0x02;
106    private static final int FILTER_PRESENCE_LEN      = 0x01;
107    private static final int FILTER_UID_PRESENT_LEN   = 0x01;
108    private static final int FOLDER_VER_COUNTER_LEN   = 0x10;
109    private static final int FILTER_MESSAGE_HANDLE_LEN= 0x10;
110    private static final int NOTIFICATION_FILTER_LEN  = 0x04;
111    private static final int CONVO_PARAMETER_MASK_LEN = 0x04;
112
113    // Default values
114    public static final int INVALID_VALUE_PARAMETER     =-1;
115    public static final int NOTIFICATION_STATUS_NO      = 0;
116    public static final int NOTIFICATION_STATUS_YES     = 1;
117    public static final int STATUS_INDICATOR_READ       = 0;
118    public static final int STATUS_INDICATOR_DELETED    = 1;
119    public static final int STATUS_VALUE_YES            = 1;
120    public static final int STATUS_VALUE_NO             = 0;
121    public static final int CHARSET_NATIVE              = 0;
122    public static final int CHARSET_UTF8                = 1;
123    public static final int FRACTION_REQUEST_FIRST      = 0;
124    public static final int FRACTION_REQUEST_NEXT       = 1;
125    public static final int FRACTION_DELIVER_MORE       = 0;
126    public static final int FRACTION_DELIVER_LAST       = 1;
127
128    public static final int FILTER_NO_SMS_GSM    = 0x01;
129    public static final int FILTER_NO_SMS_CDMA   = 0x02;
130    public static final int FILTER_NO_EMAIL      = 0x04;
131    public static final int FILTER_NO_MMS        = 0x08;
132    public static final int FILTER_NO_IM         = 0x10;
133    public static final int FILTER_MSG_TYPE_MASK = 0x1F;
134
135    private int mMaxListCount                   = INVALID_VALUE_PARAMETER;
136    private int mStartOffset                    = INVALID_VALUE_PARAMETER;
137    private int mFilterMessageType              = INVALID_VALUE_PARAMETER;
138    // It seems like these are not implemented...
139    private long mFilterPeriodBegin             = INVALID_VALUE_PARAMETER;
140    private long mFilterPeriodEnd               = INVALID_VALUE_PARAMETER;
141    private int mFilterReadStatus               = INVALID_VALUE_PARAMETER;
142    private String mFilterRecipient             = null;
143    private String mFilterOriginator            = null;
144    private int mFilterPriority                 = INVALID_VALUE_PARAMETER;
145    private int mAttachment                     = INVALID_VALUE_PARAMETER;
146    private int mTransparent                    = INVALID_VALUE_PARAMETER;
147    private int mRetry                          = INVALID_VALUE_PARAMETER;
148    private int mNewMessage                     = INVALID_VALUE_PARAMETER;
149    private int mNotificationStatus             = INVALID_VALUE_PARAMETER;
150    private long mNotificationFilter            = INVALID_VALUE_PARAMETER;
151    private int mMasInstanceId                  = INVALID_VALUE_PARAMETER;
152    private long mParameterMask                 = INVALID_VALUE_PARAMETER;
153    private int mFolderListingSize              = INVALID_VALUE_PARAMETER;
154    private int mMessageListingSize             = INVALID_VALUE_PARAMETER;
155    private int mConvoListingSize               = INVALID_VALUE_PARAMETER;
156    private int mSubjectLength                  = INVALID_VALUE_PARAMETER;
157    private int mCharset                        = INVALID_VALUE_PARAMETER;
158    private int mFractionRequest                = INVALID_VALUE_PARAMETER;
159    private int mFractionDeliver                = INVALID_VALUE_PARAMETER;
160    private int mStatusIndicator                = INVALID_VALUE_PARAMETER;
161    private int mStatusValue                    = INVALID_VALUE_PARAMETER;
162    private long mMseTime                       = INVALID_VALUE_PARAMETER;
163    // TODO: Change to use SignedLongLong?
164    private long mConvoListingVerCounterLow     = INVALID_VALUE_PARAMETER;
165    private long mConvoListingVerCounterHigh    = INVALID_VALUE_PARAMETER;
166    private long mDatabaseIdentifierLow         = INVALID_VALUE_PARAMETER;
167    private long mDatabaseIdentifierHigh        = INVALID_VALUE_PARAMETER;
168    private long mFolderVerCounterLow           = INVALID_VALUE_PARAMETER;
169    private long mFolderVerCounterHigh          = INVALID_VALUE_PARAMETER;
170    private int mPresenceAvailability           = INVALID_VALUE_PARAMETER;
171    private String mPresenceStatus              = null;
172    private long mLastActivity                  = INVALID_VALUE_PARAMETER;
173    private int mChatState                      = INVALID_VALUE_PARAMETER;
174    private SignedLongLong mFilterConvoId       = null;
175    private int mFilterPresence                 = INVALID_VALUE_PARAMETER;
176    private int mFilterUidPresent               = INVALID_VALUE_PARAMETER;
177    private SignedLongLong mChatStateConvoId    = null;
178    private long mFilterMsgHandle               = INVALID_VALUE_PARAMETER;
179    private long mConvoParameterMask            = INVALID_VALUE_PARAMETER;
180
181    /**
182     * Default constructor, used to build an application parameter object to be
183     * encoded. By default the member variables will be initialized to
184     * {@link INVALID_VALUE_PARAMETER} for values, and empty strings for String
185     * typed members.
186     */
187    public BluetoothMapAppParams() {
188    }
189
190    /**
191     * Creates an application parameter object based on a application parameter
192     * OBEX header. The content of the {@link appParam} byte array will be
193     * parsed, and its content will be stored in the member variables.
194     * {@link INVALID_VALUE_PARAMETER} can be used to determine if a value is
195     * set or not, where strings will be empty, if {@link appParam} did not
196     * contain the parameter.
197     *
198     * @param appParams
199     *            the byte array containing the application parameters OBEX
200     *            header
201     * @throws IllegalArgumentException
202     *             when a parameter does not respect the valid ranges specified
203     *             in the MAP spec.
204     * @throws ParseException
205     *             if a parameter string if formated incorrectly.
206     */
207    public BluetoothMapAppParams(final byte[] appParams)
208                 throws IllegalArgumentException, ParseException {
209        ParseParams(appParams);
210    }
211
212    /**
213     * Parse an application parameter OBEX header stored in a byte array.
214     *
215     * @param appParams
216     *            the byte array containing the application parameters OBEX
217     *            header
218     * @throws IllegalArgumentException
219     *             when a parameter does not respect the valid ranges specified
220     *             in the MAP spec.
221     * @throws ParseException
222     *             if a parameter string if formated incorrectly.
223     */
224    private void ParseParams(final byte[] appParams) throws ParseException,
225              IllegalArgumentException {
226        int i = 0;
227        int tagId, tagLength;
228        ByteBuffer appParamBuf = ByteBuffer.wrap(appParams);
229        appParamBuf.order(ByteOrder.BIG_ENDIAN);
230        while (i < appParams.length) {
231            tagId = appParams[i++] & 0xff;     // Convert to unsigned to support values above 127
232            tagLength = appParams[i++] & 0xff; // Convert to unsigned to support values above 127
233            switch (tagId) {
234            case MAX_LIST_COUNT:
235                if (tagLength != MAX_LIST_COUNT_LEN) {
236                    Log.w(TAG, "MAX_LIST_COUNT: Wrong length received: " + tagLength
237                               + " expected: " + MAX_LIST_COUNT_LEN);
238                } else {
239                    setMaxListCount(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
240                }
241                break;
242            case START_OFFSET:
243                if (tagLength != START_OFFSET_LEN) {
244                    Log.w(TAG, "START_OFFSET: Wrong length received: " + tagLength + " expected: "
245                               + START_OFFSET_LEN);
246                } else {
247                    setStartOffset(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
248                }
249                break;
250            case FILTER_MESSAGE_TYPE:
251                if (tagLength != FILTER_MESSAGE_TYPE_LEN) {
252                    Log.w(TAG, "FILTER_MESSAGE_TYPE: Wrong length received: " + tagLength
253                            + " expected: " + FILTER_MESSAGE_TYPE_LEN);
254                } else {
255                    setFilterMessageType(appParams[i] & 0x1f);
256                }
257                break;
258            case FILTER_PERIOD_BEGIN:
259                if(tagLength != 0) {
260                    setFilterPeriodBegin(new String(appParams, i, tagLength));
261                } else {
262                    Log.w(TAG, "FILTER_PERIOD_BEGIN: Wrong length received: " + tagLength +
263                            " expected to be more than 0");
264                }
265                break;
266            case FILTER_PERIOD_END:
267                if(tagLength != 0) {
268                    setFilterPeriodEnd(new String(appParams, i, tagLength));
269                } else {
270                    Log.w(TAG, "FILTER_PERIOD_END: Wrong length received: " + tagLength +
271                            " expected to be more than 0");
272                }
273                break;
274            case FILTER_READ_STATUS:
275                if (tagLength != FILTER_READ_STATUS_LEN) {
276                     Log.w(TAG, "FILTER_READ_STATUS: Wrong length received: " + tagLength +
277                             " expected: " + FILTER_READ_STATUS_LEN);
278                } else {
279                    setFilterReadStatus(appParams[i] & 0x03); // Lower two bits
280                }
281                break;
282            case FILTER_RECIPIENT:
283                if(tagLength != 0) {
284                    setFilterRecipient(new String(appParams, i, tagLength));
285                } else {
286                    Log.w(TAG, "FILTER_RECIPIENT: Wrong length received: " + tagLength +
287                            " expected to be more than 0");
288                }
289                break;
290            case FILTER_ORIGINATOR:
291                if(tagLength != 0) {
292                    setFilterOriginator(new String(appParams, i, tagLength));
293                } else {
294                    Log.w(TAG, "FILTER_ORIGINATOR: Wrong length received: " + tagLength +
295                            " expected to be more than 0");
296                }
297                break;
298            case FILTER_PRIORITY:
299                if (tagLength != FILTER_PRIORITY_LEN) {
300                     Log.w(TAG, "FILTER_PRIORITY: Wrong length received: " + tagLength +
301                             " expected: " + FILTER_PRIORITY_LEN);
302                } else {
303                    setFilterPriority(appParams[i] & 0x03); // Lower two bits
304                }
305                break;
306            case ATTACHMENT:
307                if (tagLength != ATTACHMENT_LEN) {
308                     Log.w(TAG, "ATTACHMENT: Wrong length received: " + tagLength + " expected: "
309                             + ATTACHMENT_LEN);
310                } else {
311                    setAttachment(appParams[i] & 0x01); // Lower bit
312                }
313                break;
314            case TRANSPARENT:
315                if (tagLength != TRANSPARENT_LEN) {
316                     Log.w(TAG, "TRANSPARENT: Wrong length received: " + tagLength + " expected: "
317                             + TRANSPARENT_LEN);
318                } else {
319                    setTransparent(appParams[i] & 0x01); // Lower bit
320                }
321                break;
322            case RETRY:
323                if (tagLength != RETRY_LEN) {
324                     Log.w(TAG, "RETRY: Wrong length received: " + tagLength + " expected: "
325                             + RETRY_LEN);
326                } else {
327                    setRetry(appParams[i] & 0x01); // Lower bit
328                }
329                break;
330            case NEW_MESSAGE:
331                if (tagLength != NEW_MESSAGE_LEN) {
332                     Log.w(TAG, "NEW_MESSAGE: Wrong length received: " + tagLength + " expected: "
333                             + NEW_MESSAGE_LEN);
334                } else {
335                    setNewMessage(appParams[i] & 0x01); // Lower bit
336                }
337                break;
338            case NOTIFICATION_STATUS:
339                if (tagLength != NOTIFICATION_STATUS_LEN) {
340                     Log.w(TAG, "NOTIFICATION_STATUS: Wrong length received: " + tagLength +
341                             " expected: " + NOTIFICATION_STATUS_LEN);
342                } else {
343                    setNotificationStatus(appParams[i] & 0x01); // Lower bit
344                }
345                break;
346            case NOTIFICATION_FILTER:
347                if (tagLength != NOTIFICATION_FILTER_LEN) {
348                     Log.w(TAG, "NOTIFICATION_FILTER: Wrong length received: " + tagLength +
349                             " expected: " + NOTIFICATION_FILTER_LEN);
350                } else {
351                    setNotificationFilter(appParamBuf.getInt(i) & 0xffffffffL); // 4 bytes
352                }
353                break;
354            case MAS_INSTANCE_ID:
355                if (tagLength != MAS_INSTANCE_ID_LEN) {
356                    Log.w(TAG, "MAS_INSTANCE_ID: Wrong length received: " + tagLength +
357                            " expected: " + MAS_INSTANCE_ID_LEN);
358                } else {
359                    setMasInstanceId(appParams[i] & 0xff);
360                }
361                break;
362            case PARAMETER_MASK:
363                if (tagLength != PARAMETER_MASK_LEN) {
364                    Log.w(TAG, "PARAMETER_MASK: Wrong length received: " + tagLength +
365                            " expected: " + PARAMETER_MASK_LEN);
366                } else {
367                    setParameterMask(appParamBuf.getInt(i) & 0xffffffffL); // Make it unsigned
368                }
369                break;
370            case FOLDER_LISTING_SIZE:
371                if (tagLength != FOLDER_LISTING_SIZE_LEN) {
372                    Log.w(TAG, "FOLDER_LISTING_SIZE: Wrong length received: " + tagLength +
373                            " expected: " + FOLDER_LISTING_SIZE_LEN);
374                } else {
375                    setFolderListingSize(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
376                }
377                break;
378            case MESSAGE_LISTING_SIZE:
379                if (tagLength != MESSAGE_LISTING_SIZE_LEN) {
380                    Log.w(TAG, "MESSAGE_LISTING_SIZE: Wrong length received: " + tagLength +
381                            " expected: " + MESSAGE_LISTING_SIZE_LEN);
382                } else {
383                    setMessageListingSize(appParamBuf.getShort(i) & 0xffff); // Make it unsigned
384                }
385                break;
386            case SUBJECT_LENGTH:
387                if (tagLength != SUBJECT_LENGTH_LEN) {
388                    Log.w(TAG, "SUBJECT_LENGTH: Wrong length received: " + tagLength +
389                            " expected: " + SUBJECT_LENGTH_LEN);
390                } else {
391                    setSubjectLength(appParams[i] & 0xff);
392                }
393                break;
394            case CHARSET:
395                if (tagLength != CHARSET_LEN) {
396                    Log.w(TAG, "CHARSET: Wrong length received: " + tagLength + " expected: "
397                            + CHARSET_LEN);
398                } else {
399                    setCharset(appParams[i] & 0x01); // Lower bit
400                }
401                break;
402            case FRACTION_REQUEST:
403                if (tagLength != FRACTION_REQUEST_LEN) {
404                    Log.w(TAG, "FRACTION_REQUEST: Wrong length received: " + tagLength +
405                            " expected: " + FRACTION_REQUEST_LEN);
406                } else {
407                    setFractionRequest(appParams[i] & 0x01); // Lower bit
408                }
409                break;
410            case FRACTION_DELIVER:
411                if (tagLength != FRACTION_DELIVER_LEN) {
412                    Log.w(TAG, "FRACTION_DELIVER: Wrong length received: " + tagLength +
413                            " expected: " + FRACTION_DELIVER_LEN);
414                } else {
415                    setFractionDeliver(appParams[i] & 0x01); // Lower bit
416                }
417                break;
418            case STATUS_INDICATOR:
419                if (tagLength != STATUS_INDICATOR_LEN) {
420                    Log.w(TAG, "STATUS_INDICATOR: Wrong length received: " + tagLength +
421                            " expected: " + STATUS_INDICATOR_LEN);
422                } else {
423                    setStatusIndicator(appParams[i] & 0x01); // Lower bit
424                }
425                break;
426            case STATUS_VALUE:
427                if (tagLength != STATUS_VALUE_LEN) {
428                    Log.w(TAG, "STATUS_VALUER: Wrong length received: " + tagLength + " expected: "
429                            + STATUS_VALUE_LEN);
430                } else {
431                    setStatusValue(appParams[i] & 0x01); // Lower bit
432                }
433                break;
434            case MSE_TIME:
435                setMseTime(new String(appParams, i, tagLength));
436                break;
437            case DATABASE_INDETIFIER:
438                if((tagLength != DATABASE_INDETIFIER_LEN)){
439                    Log.w(TAG, "DATABASE_IDENTIFIER: Wrong length received: " + tagLength +
440                            " expected: " + DATABASE_INDETIFIER_LEN);
441                } else {
442                    setDatabaseIdentifier(appParamBuf.getLong(i)/*MSB*/,
443                            appParamBuf.getLong(i+8)/*LSB*/);
444                }
445                break;
446            case CONVO_LIST_VER_COUNTER:
447                if((tagLength != CONVO_LIST_VER_COUNTER_LEN)){
448                    Log.w(TAG, "CONVO_LIST_VER_COUNTER: Wrong length received: " + tagLength +
449                            " expected: "  + CONVO_LIST_VER_COUNTER_LEN);
450                } else {
451                    setConvoListingVerCounter(appParamBuf.getLong(i)/*MSB*/,
452                            appParamBuf.getLong(i+8)/*LSB*/);
453                }
454                break;
455            case PRESENCE_AVAILABLE:
456                if((tagLength != PRESENCE_AVAILABLE_LEN)){
457                    Log.w(TAG, "PRESENCE_AVAILABLE: Wrong length received: " + tagLength +
458                            " expected: "  + PRESENCE_AVAILABLE_LEN);
459                } else {
460                    setPresenceAvailability(appParams[i]);
461                }
462                break;
463            case PRESENCE_TEXT:
464                if(tagLength != 0) {
465                    setPresenceStatus(new String(appParams, i, tagLength));
466                } else
467                    Log.w(TAG, "PRESENCE_STATUS: Wrong length received: " + tagLength +
468                            " expected to be more than 0");
469                break;
470            case LAST_ACTIVITY:
471                if(tagLength != 0) {
472                    setLastActivity(new String(appParams, i, tagLength));
473                } else
474                    Log.w(TAG, "LAST_ACTIVITY: Wrong length received: " + tagLength +
475                            " expected to be more than 0");
476                break;
477            case CHAT_STATE:
478                if((tagLength != CHAT_STATE_LEN)){
479                    Log.w(TAG, "CHAT_STATE: Wrong length received: " + tagLength +
480                            " expected: "  + CHAT_STATE_LEN);
481                } else {
482                    setChatState(appParams[i]);
483                }
484                break;
485            case FILTER_CONVO_ID:
486                if((tagLength != 0) && (tagLength <= FILTER_CONVO_ID_LEN)){
487                    setFilterConvoId(new String(appParams, i, tagLength));
488                } else {
489                    Log.w(TAG, "FILTER_CONVO_ID: Wrong length received: " + tagLength +
490                        " expected: "  + FILTER_CONVO_ID_LEN);
491                }
492                break;
493            case CONVO_LISTING_SIZE:
494                if(tagLength != CONVO_LISTING_SIZE_LEN){
495                    Log.w(TAG, "LISTING_SIZE: Wrong length received: "+ tagLength+" expected: "+
496                            CONVO_LISTING_SIZE_LEN);
497
498                } else {
499                    setConvoListingSize(appParamBuf.getShort(i) & 0xffff);
500                }
501                break;
502            case FILTER_PRESENCE:
503                if((tagLength != FILTER_PRESENCE_LEN)){
504                    Log.w(TAG, "FILTER_PRESENCE: Wrong length received: " + tagLength +
505                            " expected: "  + FILTER_PRESENCE_LEN);
506                } else {
507                    setFilterPresence(appParams[i]);
508                }
509                break;
510            case FILTER_UID_PRESENT:
511                if((tagLength != FILTER_UID_PRESENT_LEN)){
512                    Log.w(TAG, "FILTER_UID_PRESENT: Wrong length received: " + tagLength +
513                            " expected: "  + FILTER_UID_PRESENT_LEN);
514                } else {
515                    setFilterUidPresent(appParams[i]&0x1);
516                }
517                break;
518            case CHAT_STATE_CONVO_ID:
519                if((tagLength != CHAT_STATE_CONVO_ID_LEN)){
520                    Log.w(TAG, "CHAT_STATE_CONVO_ID: Wrong length received: " + tagLength +
521                            " expected: "  + CHAT_STATE_CONVO_ID_LEN);
522                } else {
523                    /* TODO: Is this correct convoId handling? */
524                    setChatStateConvoId(appParamBuf.getLong(i)/*MSB*/,
525                                        appParamBuf.getLong(i+8)/*LSB*/);
526                    Log.d(TAG, "CHAT_STATE_CONVO_ID: convo id " +
527                        "MSB=" + BluetoothMapUtils.getLongAsString(appParamBuf.getLong(i)) +
528                        ", LSB(+8)=" + BluetoothMapUtils.getLongAsString(appParamBuf.getLong(i+8)));
529
530                }
531                break;
532            case FOLDER_VER_COUNTER:
533                break;
534            case FILTER_MESSAGE_HANDLE:
535                if((tagLength != 0 && tagLength <= FILTER_MESSAGE_HANDLE_LEN)){
536                    setFilterMsgHandle(new String(appParams, i, tagLength));
537                } else {
538                    Log.w(TAG, "FILTER_MESSAGE_HANDLE: Wrong length received: " + tagLength +
539                            " expected: "  + FILTER_MESSAGE_HANDLE_LEN);
540                }
541
542                break;
543            case CONVO_PARAMETER_MASK:
544                if (tagLength != CONVO_PARAMETER_MASK_LEN) {
545                    Log.w(TAG, "CONVO_PARAMETER_MASK: Wrong length received: " + tagLength +
546                            " expected: " + CONVO_PARAMETER_MASK_LEN);
547                } else {
548                    setConvoParameterMask(appParamBuf.getInt(i) & 0xffffffffL); // Make it unsigned
549                }
550                break;
551            default:
552                // Just skip unknown Tags, no need to report error
553                Log.w(TAG, "Unknown TagId received ( 0x" + Integer.toString(tagId, 16)
554                           + "), skipping...");
555                break;
556            }
557            i += tagLength; // Offset to next TagId
558        }
559    }
560
561    /**
562     * Get the approximate length needed to store the appParameters in a byte
563     * array.
564     *
565     * @return the length in bytes
566     * @throws UnsupportedEncodingException
567     *             if the platform does not support UTF-8 encoding.
568     */
569    private int getParamMaxLength() throws UnsupportedEncodingException {
570        int length = 0;
571        length += 38 * 2; // tagId + tagLength
572        length += 33+4*16; // fixed sizes TODO: Update when spec is ready
573        length += getFilterPeriodBegin() == INVALID_VALUE_PARAMETER ? 0 : 20;
574        length += getFilterPeriodEnd() == INVALID_VALUE_PARAMETER ? 0 : 20;
575        if (getFilterRecipient() != null)
576            length += getFilterRecipient().getBytes("UTF-8").length;
577        if (getFilterOriginator() != null)
578            length += getFilterOriginator().getBytes("UTF-8").length;
579        length += getMseTime() == INVALID_VALUE_PARAMETER ? 0 : 20;
580        if(getPresenceStatus() != null)
581            length += getPresenceStatus().getBytes("UTF-8").length;
582        length += (getLastActivity() == INVALID_VALUE_PARAMETER) ? 0 : 20;
583        return length;
584    }
585
586    /**
587     * Encode the application parameter object to a byte array.
588     *
589     * @return a byte Array representation of the application parameter object.
590     * @throws UnsupportedEncodingException
591     *             if the platform does not support UTF-8 encoding.
592     */
593    public byte[] EncodeParams() throws UnsupportedEncodingException {
594        ByteBuffer appParamBuf = ByteBuffer.allocate(getParamMaxLength());
595        appParamBuf.order(ByteOrder.BIG_ENDIAN);
596        byte[] retBuf;
597
598        if (getMaxListCount() != INVALID_VALUE_PARAMETER) {
599            appParamBuf.put((byte) MAX_LIST_COUNT);
600            appParamBuf.put((byte) MAX_LIST_COUNT_LEN);
601            appParamBuf.putShort((short) getMaxListCount());
602        }
603        if (getStartOffset() != INVALID_VALUE_PARAMETER) {
604            appParamBuf.put((byte) START_OFFSET);
605            appParamBuf.put((byte) START_OFFSET_LEN);
606            appParamBuf.putShort((short) getStartOffset());
607        }
608        if (getFilterMessageType() != INVALID_VALUE_PARAMETER) {
609            appParamBuf.put((byte) FILTER_MESSAGE_TYPE);
610            appParamBuf.put((byte) FILTER_MESSAGE_TYPE_LEN);
611            appParamBuf.put((byte) getFilterMessageType());
612        }
613        if (getFilterPeriodBegin() != INVALID_VALUE_PARAMETER) {
614            appParamBuf.put((byte) FILTER_PERIOD_BEGIN);
615            appParamBuf.put((byte) getFilterPeriodBeginString().getBytes("UTF-8").length);
616            appParamBuf.put(getFilterPeriodBeginString().getBytes("UTF-8"));
617        }
618        if (getFilterPeriodEnd() != INVALID_VALUE_PARAMETER) {
619            appParamBuf.put((byte) FILTER_PERIOD_END);
620            appParamBuf.put((byte) getFilterPeriodEndString().getBytes("UTF-8").length);
621            appParamBuf.put(getFilterPeriodEndString().getBytes("UTF-8"));
622        }
623        if (getFilterReadStatus() != INVALID_VALUE_PARAMETER) {
624            appParamBuf.put((byte) FILTER_READ_STATUS);
625            appParamBuf.put((byte) FILTER_READ_STATUS_LEN);
626            appParamBuf.put((byte) getFilterReadStatus());
627        }
628        if (getFilterRecipient() != null) {
629            appParamBuf.put((byte) FILTER_RECIPIENT);
630            appParamBuf.put((byte) getFilterRecipient().getBytes("UTF-8").length);
631            appParamBuf.put(getFilterRecipient().getBytes("UTF-8"));
632        }
633        if (getFilterOriginator() != null) {
634            appParamBuf.put((byte) FILTER_ORIGINATOR);
635            appParamBuf.put((byte) getFilterOriginator().getBytes("UTF-8").length);
636            appParamBuf.put(getFilterOriginator().getBytes("UTF-8"));
637        }
638        if (getFilterPriority() != INVALID_VALUE_PARAMETER) {
639            appParamBuf.put((byte) FILTER_PRIORITY);
640            appParamBuf.put((byte) FILTER_PRIORITY_LEN);
641            appParamBuf.put((byte) getFilterPriority());
642        }
643        if (getAttachment() != INVALID_VALUE_PARAMETER) {
644            appParamBuf.put((byte) ATTACHMENT);
645            appParamBuf.put((byte) ATTACHMENT_LEN);
646            appParamBuf.put((byte) getAttachment());
647        }
648        if (getTransparent() != INVALID_VALUE_PARAMETER) {
649            appParamBuf.put((byte) TRANSPARENT);
650            appParamBuf.put((byte) TRANSPARENT_LEN);
651            appParamBuf.put((byte) getTransparent());
652        }
653        if (getRetry() != INVALID_VALUE_PARAMETER) {
654            appParamBuf.put((byte) RETRY);
655            appParamBuf.put((byte) RETRY_LEN);
656            appParamBuf.put((byte) getRetry());
657        }
658        if (getNewMessage() != INVALID_VALUE_PARAMETER) {
659            appParamBuf.put((byte) NEW_MESSAGE);
660            appParamBuf.put((byte) NEW_MESSAGE_LEN);
661            appParamBuf.put((byte) getNewMessage());
662        }
663        if (getNotificationStatus() != INVALID_VALUE_PARAMETER) {
664            appParamBuf.put((byte) NOTIFICATION_STATUS);
665            appParamBuf.put((byte) NOTIFICATION_STATUS_LEN);
666            appParamBuf.putShort((short) getNotificationStatus());
667        }
668        if (getNotificationFilter() != INVALID_VALUE_PARAMETER) {
669            appParamBuf.put((byte) NOTIFICATION_FILTER);
670            appParamBuf.put((byte) NOTIFICATION_FILTER_LEN);
671            appParamBuf.putInt((int) getNotificationFilter());
672        }
673        if (getMasInstanceId() != INVALID_VALUE_PARAMETER) {
674            appParamBuf.put((byte) MAS_INSTANCE_ID);
675            appParamBuf.put((byte) MAS_INSTANCE_ID_LEN);
676            appParamBuf.put((byte) getMasInstanceId());
677        }
678        if (getParameterMask() != INVALID_VALUE_PARAMETER) {
679            appParamBuf.put((byte) PARAMETER_MASK);
680            appParamBuf.put((byte) PARAMETER_MASK_LEN);
681            appParamBuf.putInt((int) getParameterMask());
682        }
683        if (getFolderListingSize() != INVALID_VALUE_PARAMETER) {
684            appParamBuf.put((byte) FOLDER_LISTING_SIZE);
685            appParamBuf.put((byte) FOLDER_LISTING_SIZE_LEN);
686            appParamBuf.putShort((short) getFolderListingSize());
687        }
688        if (getMessageListingSize() != INVALID_VALUE_PARAMETER) {
689            appParamBuf.put((byte) MESSAGE_LISTING_SIZE);
690            appParamBuf.put((byte) MESSAGE_LISTING_SIZE_LEN);
691            appParamBuf.putShort((short) getMessageListingSize());
692        }
693        if (getSubjectLength() != INVALID_VALUE_PARAMETER) {
694            appParamBuf.put((byte) SUBJECT_LENGTH);
695            appParamBuf.put((byte) SUBJECT_LENGTH_LEN);
696            appParamBuf.put((byte) getSubjectLength());
697        }
698        if (getCharset() != INVALID_VALUE_PARAMETER) {
699            appParamBuf.put((byte) CHARSET);
700            appParamBuf.put((byte) CHARSET_LEN);
701            appParamBuf.put((byte) getCharset());
702        }
703        if (getFractionRequest() != INVALID_VALUE_PARAMETER) {
704            appParamBuf.put((byte) FRACTION_REQUEST);
705            appParamBuf.put((byte) FRACTION_REQUEST_LEN);
706            appParamBuf.put((byte) getFractionRequest());
707        }
708        if (getFractionDeliver() != INVALID_VALUE_PARAMETER) {
709            appParamBuf.put((byte) FRACTION_DELIVER);
710            appParamBuf.put((byte) FRACTION_DELIVER_LEN);
711            appParamBuf.put((byte) getFractionDeliver());
712        }
713        if (getStatusIndicator() != INVALID_VALUE_PARAMETER) {
714            appParamBuf.put((byte) STATUS_INDICATOR);
715            appParamBuf.put((byte) STATUS_INDICATOR_LEN);
716            appParamBuf.put((byte) getStatusIndicator());
717        }
718        if (getStatusValue() != INVALID_VALUE_PARAMETER) {
719            appParamBuf.put((byte) STATUS_VALUE);
720            appParamBuf.put((byte) STATUS_VALUE_LEN);
721            appParamBuf.put((byte) getStatusValue());
722        }
723        if (getMseTime() != INVALID_VALUE_PARAMETER) {
724            appParamBuf.put((byte) MSE_TIME);
725            appParamBuf.put((byte) getMseTimeString().getBytes("UTF-8").length);
726            appParamBuf.put(getMseTimeString().getBytes("UTF-8"));
727        }
728        // Note: New for IM
729        if (getDatabaseIdentifier() != null) {
730            appParamBuf.put((byte)DATABASE_INDETIFIER);
731            appParamBuf.put((byte)DATABASE_INDETIFIER_LEN);
732            appParamBuf.put(getDatabaseIdentifier());
733        }
734        if (getConvoListingVerCounter() != null) {
735            appParamBuf.put((byte)CONVO_LIST_VER_COUNTER);
736            appParamBuf.put((byte)CONVO_LIST_VER_COUNTER_LEN);
737            appParamBuf.put(getConvoListingVerCounter());
738        }
739        if (getPresenceAvailability() != INVALID_VALUE_PARAMETER) {
740            appParamBuf.put((byte)PRESENCE_AVAILABLE);
741            appParamBuf.put((byte)PRESENCE_AVAILABLE_LEN);
742            appParamBuf.putInt((int)getPresenceAvailability());
743        }
744        if (getPresenceStatus()!= null) {
745            appParamBuf.put((byte)PRESENCE_TEXT);
746            appParamBuf.put((byte)getPresenceStatus().getBytes("UTF-8").length);
747            appParamBuf.put(getPresenceStatus().getBytes());
748        }
749        if (getLastActivity() != INVALID_VALUE_PARAMETER) {
750            appParamBuf.put((byte)LAST_ACTIVITY);
751            appParamBuf.put((byte)getLastActivityString().getBytes("UTF-8").length);
752            appParamBuf.put(getLastActivityString().getBytes());
753        }
754        if (getChatState() != INVALID_VALUE_PARAMETER) {
755            appParamBuf.put((byte)CHAT_STATE);
756            appParamBuf.put((byte)CHAT_STATE_LEN);
757            appParamBuf.putShort((short)getChatState());
758        }
759        if (getFilterConvoId() != null) {
760            appParamBuf.put((byte)FILTER_CONVO_ID);
761            appParamBuf.put((byte)FILTER_CONVO_ID_LEN);
762            appParamBuf.putLong(getFilterConvoId().getMostSignificantBits());
763            appParamBuf.putLong(getFilterConvoId().getLeastSignificantBits());
764        }
765        if (getConvoListingSize() != INVALID_VALUE_PARAMETER) {
766            appParamBuf.put((byte)CONVO_LISTING_SIZE);
767            appParamBuf.put((byte)CONVO_LISTING_SIZE_LEN);
768            appParamBuf.putShort((short)getConvoListingSize());
769        }
770        if (getFilterPresence() != INVALID_VALUE_PARAMETER) {
771            appParamBuf.put((byte)FILTER_PRESENCE);
772            appParamBuf.put((byte)FILTER_PRESENCE_LEN);
773            appParamBuf.putShort((short)getFilterPresence());
774        }
775        if (getFilterUidPresent() != INVALID_VALUE_PARAMETER) {
776            appParamBuf.put((byte)FILTER_UID_PRESENT);
777            appParamBuf.put((byte)FILTER_UID_PRESENT_LEN);
778            appParamBuf.putShort((short)getFilterUidPresent());
779        }
780        if (getChatStateConvoId() != null) {
781            appParamBuf.put((byte)CHAT_STATE_CONVO_ID);
782            appParamBuf.put((byte)CHAT_STATE_CONVO_ID_LEN);
783            appParamBuf.putLong(getChatStateConvoId().getMostSignificantBits());
784            appParamBuf.putLong(getChatStateConvoId().getLeastSignificantBits());
785        }
786        if (getFolderVerCounter() != null) {
787            appParamBuf.put((byte)FOLDER_VER_COUNTER);
788            appParamBuf.put((byte)FOLDER_VER_COUNTER_LEN);
789            appParamBuf.put(getFolderVerCounter());
790        }
791        if (getFilterMsgHandle() != INVALID_VALUE_PARAMETER) {
792            appParamBuf.put((byte)FILTER_MESSAGE_HANDLE);
793            appParamBuf.put((byte)FILTER_MESSAGE_HANDLE_LEN);
794            appParamBuf.putLong(getFilterMsgHandle());
795        }
796        if (getConvoParameterMask() != INVALID_VALUE_PARAMETER) {
797            appParamBuf.put((byte) CONVO_PARAMETER_MASK);
798            appParamBuf.put((byte) CONVO_PARAMETER_MASK_LEN);
799            appParamBuf.putInt((int) getConvoParameterMask());
800        }
801
802        // We need to reduce the length of the array to match the content
803        retBuf = Arrays.copyOfRange(appParamBuf.array(), appParamBuf.arrayOffset(),
804                                    appParamBuf.arrayOffset() + appParamBuf.position());
805        return retBuf;
806    }
807
808    public int getMaxListCount() {
809        return mMaxListCount;
810    }
811
812    public void setMaxListCount(int maxListCount) throws IllegalArgumentException {
813        if (maxListCount < 0 || maxListCount > 0xFFFF)
814            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
815        this.mMaxListCount = maxListCount;
816    }
817
818    public int getStartOffset() {
819        return mStartOffset;
820    }
821
822    public void setStartOffset(int startOffset) throws IllegalArgumentException {
823        if (startOffset < 0 || startOffset > 0xFFFF)
824            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
825        this.mStartOffset = startOffset;
826    }
827
828    public int getFilterMessageType() {
829        return mFilterMessageType;
830    }
831
832    public void setFilterMessageType(int filterMessageType) throws IllegalArgumentException {
833        if (filterMessageType < 0 || filterMessageType > 0x001F)
834            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x001F");
835        this.mFilterMessageType = filterMessageType;
836    }
837
838    public long getFilterPeriodBegin() {
839        return mFilterPeriodBegin;
840    }
841
842    public String getFilterPeriodBeginString() {
843        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
844        Date date = new Date(mFilterPeriodBegin);
845        return format.format(date); // Format to YYYYMMDDTHHMMSS local time
846    }
847
848    public void setFilterPeriodBegin(long filterPeriodBegin) {
849        this.mFilterPeriodBegin = filterPeriodBegin;
850    }
851
852    public void setFilterPeriodBegin(String filterPeriodBegin) throws ParseException {
853        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
854        Date date = format.parse(filterPeriodBegin);
855        this.mFilterPeriodBegin = date.getTime();
856    }
857
858    public long getFilterLastActivityBegin() {
859        return mFilterPeriodBegin;
860    }
861    public String getFilterLastActivityBeginString() {
862        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
863        Date date = new Date(mFilterPeriodBegin);
864        return format.format(date); // Format to YYYYMMDDTHHMMSS local time
865    }
866    public void setFilterLastActivityBegin(long filterPeriodBegin) {
867        this.mFilterPeriodBegin = filterPeriodBegin;
868    }
869
870    public void setFilterLastActivityBegin(String filterPeriodBegin)throws ParseException {
871        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
872        Date date = format.parse(filterPeriodBegin);
873        this.mFilterPeriodBegin = date.getTime();
874    }
875    public long getFilterPeriodEnd() {
876        return mFilterPeriodEnd;
877    }
878    public long getFilterLastActivityEnd() {
879        return mFilterPeriodEnd;
880    }
881
882    public String getFilterLastActivityEndString() {
883        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
884        Date date = new Date(mFilterPeriodEnd);
885        return format.format(date); // Format to YYYYMMDDTHHMMSS local time
886    }
887
888    public void setFilterLastActivityEnd(long filterPeriodEnd) {
889        this.mFilterPeriodEnd= filterPeriodEnd; //er reuse the same
890    }
891
892    public void setFilterPeriodEnd(String filterPeriodEnd) throws ParseException {
893        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
894        Date date = format.parse(filterPeriodEnd);
895        this.mFilterPeriodEnd = date.getTime();
896    }
897    public String getFilterPeriodEndString() {
898        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
899        Date date = new Date(mFilterPeriodEnd);
900        return format.format(date); // Format to YYYYMMDDTHHMMSS local time
901    }
902
903    public void setFilterPeriodEnd(long filterPeriodEnd) {
904        this.mFilterPeriodEnd = filterPeriodEnd;
905    }
906
907    public void setFilterLastActivityEnd(String filterPeriodEnd) throws ParseException {
908        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
909        Date date = format.parse(filterPeriodEnd);
910        this.mFilterPeriodEnd = date.getTime();
911    }
912    public int getFilterReadStatus() {
913        return mFilterReadStatus;
914    }
915
916    public void setFilterReadStatus(int filterReadStatus) throws IllegalArgumentException {
917        if (filterReadStatus < 0 || filterReadStatus > 0x0002)
918            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0002");
919        this.mFilterReadStatus = filterReadStatus;
920    }
921
922    public String getFilterRecipient() {
923        return mFilterRecipient;
924    }
925
926    public void setFilterRecipient(String filterRecipient) {
927        this.mFilterRecipient = filterRecipient;
928    }
929
930    public String getFilterOriginator() {
931        return mFilterOriginator;
932    }
933
934    public void setFilterOriginator(String filterOriginator) {
935        this.mFilterOriginator = filterOriginator;
936    }
937
938    public int getFilterPriority() {
939        return mFilterPriority;
940    }
941
942    public void setFilterPriority(int filterPriority) throws IllegalArgumentException {
943        if (filterPriority < 0 || filterPriority > 0x0002)
944            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0002");
945        this.mFilterPriority = filterPriority;
946    }
947
948    public void setDatabaseIdentifier(long idHigh, long idLow) {
949        this.mDatabaseIdentifierHigh = idHigh;
950        this.mDatabaseIdentifierLow  = idLow;
951    }
952
953    public byte[] getDatabaseIdentifier() {
954        if(mDatabaseIdentifierLow != INVALID_VALUE_PARAMETER
955                && mDatabaseIdentifierHigh != INVALID_VALUE_PARAMETER) {
956            ByteBuffer ret = ByteBuffer.allocate(16);
957            ret.putLong(mDatabaseIdentifierHigh);
958            ret.putLong(mDatabaseIdentifierLow);
959                return ret.array();
960        }else return null;
961    }
962
963    public void setConvoListingVerCounter(long countLow, long countHigh) {
964        this.mConvoListingVerCounterHigh = countHigh;
965        this.mConvoListingVerCounterLow  = countLow;
966    }
967
968    public byte[] getConvoListingVerCounter(){
969        if(mConvoListingVerCounterHigh != INVALID_VALUE_PARAMETER &&
970            mConvoListingVerCounterLow != INVALID_VALUE_PARAMETER) {
971            ByteBuffer ret = ByteBuffer.allocate(16);
972            ret.putLong(mConvoListingVerCounterHigh);
973            ret.putLong(mConvoListingVerCounterLow);
974            return ret.array();
975            } else return null;
976    }
977
978    public void setFolderVerCounter(long countLow, long countHigh) {
979        this.mFolderVerCounterHigh = countHigh;
980        this.mFolderVerCounterLow = countLow;
981    }
982
983    public byte[] getFolderVerCounter(){
984        if(mFolderVerCounterHigh != INVALID_VALUE_PARAMETER &&
985                mFolderVerCounterLow != INVALID_VALUE_PARAMETER) {
986            ByteBuffer ret = ByteBuffer.allocate(16);
987            ret.putLong(mFolderVerCounterHigh);
988            ret.putLong(mFolderVerCounterLow);
989            return ret.array();
990        } else return null;
991    }
992
993    public SignedLongLong getChatStateConvoId(){
994        return mChatStateConvoId;
995    }
996
997    public byte[] getChatStateConvoIdByteArray() {
998        if(mChatStateConvoId != null) {
999            ByteBuffer ret = ByteBuffer.allocate(16);
1000            ret.putLong(mChatStateConvoId.getMostSignificantBits());
1001            ret.putLong(mChatStateConvoId.getLeastSignificantBits());
1002            return ret.array();
1003        } else return null;
1004    }
1005
1006    public String getChatStateConvoIdString() {
1007        String str = null;
1008        str = new String(this.getChatStateConvoIdByteArray());
1009        return str;
1010    }
1011
1012    public void setChatStateConvoId(long idHigh, long idLow) {
1013        mChatStateConvoId = new SignedLongLong(idLow, idHigh);
1014    }
1015
1016    public void setFilterMsgHandle(String handle) {
1017            try {
1018                mFilterMsgHandle = BluetoothMapUtils.getLongFromString(handle);
1019            } catch (UnsupportedEncodingException e) {
1020                Log.w(TAG,"Error creating long from handle string", e);
1021            }
1022    }
1023
1024    public long getFilterMsgHandle(){
1025        return mFilterMsgHandle;
1026    }
1027
1028    public String getFilterMsgHandleString() {
1029        String str = null;
1030        if(mFilterMsgHandle != INVALID_VALUE_PARAMETER) {
1031            str = BluetoothMapUtils.getLongAsString(mFilterMsgHandle);
1032        }
1033        return str;
1034    }
1035
1036    public int getFilterUidPresent() {
1037        return mFilterUidPresent;
1038    }
1039
1040    public void setFilterUidPresent(int present) {
1041        if (present < 0 || present > 0x00FF)
1042            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
1043        this.mFilterUidPresent = present;
1044    }
1045
1046    public int getFilterPresence() {
1047        return mFilterPresence;
1048    }
1049
1050
1051
1052    public SignedLongLong getFilterConvoId(){
1053        return mFilterConvoId;
1054    }
1055
1056    /**
1057     * Get a decimal representation of the lower bits of the ConvoId - used for queries.
1058     * The upper bits are used for convo-type.
1059     * @return decimal representation of the convo ID.
1060     */
1061    public String getFilterConvoIdString() {
1062        String str = null;
1063        if(mFilterConvoId != null) {
1064            str = BluetoothMapUtils.getLongAsString(mFilterConvoId.getLeastSignificantBits());
1065        }
1066        return str;
1067    }
1068
1069
1070    public void setFilterConvoId(String id) {
1071        try {
1072            mFilterConvoId = SignedLongLong.fromString(id);
1073        } catch (UnsupportedEncodingException e) {
1074            Log.w(TAG,"Error creating long from id string", e);
1075        }
1076    }
1077
1078
1079    public void setChatState(int state) {
1080        if (state < 0 || state > 0x00FF)
1081            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
1082        this.mChatState = state;
1083    }
1084
1085    public int getChatState() {
1086        return mChatState;
1087    }
1088
1089    public long getLastActivity(){
1090        return this.mLastActivity;
1091    }
1092    public String getLastActivityString(){
1093        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
1094        Date date = new Date(mLastActivity);
1095        return format.format(date); // Format to YYYYMMDDTHHMMSS local time
1096    }
1097    public void setLastActivity(long last){
1098        this.mLastActivity = last;
1099    }
1100    public void setLastActivity(String lastActivity) throws ParseException {
1101        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
1102        Date date = format.parse(lastActivity);
1103        this.mLastActivity = date.getTime();
1104    }
1105
1106    public void setPresenceStatus(String status){
1107        this.mPresenceStatus = status;
1108    }
1109    public String getPresenceStatus(){
1110        return this.mPresenceStatus;
1111    }
1112
1113    public void setFilterPresence(int presence) {
1114        if (presence < 0 || presence > 0xFFFF)
1115            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
1116        this.mFilterPresence = presence;
1117    }
1118
1119    public void setPresenceAvailability(int availability) {
1120        if (availability < 0 || availability > 0x00FF)
1121            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
1122        this.mPresenceAvailability = availability;
1123    }
1124
1125    public int getPresenceAvailability() {
1126        return mPresenceAvailability;
1127    }
1128
1129    public int getSubjectLength() {
1130        return mSubjectLength;
1131    }
1132    public int getAttachment() {
1133        return mAttachment;
1134    }
1135
1136    public void setAttachment(int attachment) throws IllegalArgumentException {
1137        if (attachment < 0 || attachment > 0x0001)
1138            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1139        this.mAttachment = attachment;
1140    }
1141
1142    public int getTransparent() {
1143        return mTransparent;
1144    }
1145
1146    public void setTransparent(int transparent) throws IllegalArgumentException {
1147        if (transparent < 0 || transparent > 0x0001)
1148            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1149        this.mTransparent = transparent;
1150    }
1151
1152    public int getRetry() {
1153        return mRetry;
1154    }
1155
1156    public void setRetry(int retry) throws IllegalArgumentException {
1157        if (retry < 0 || retry > 0x0001)
1158            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1159        this.mRetry = retry;
1160    }
1161
1162    public int getNewMessage() {
1163        return mNewMessage;
1164    }
1165
1166    public void setNewMessage(int newMessage) throws IllegalArgumentException {
1167        if (newMessage < 0 || newMessage > 0x0001)
1168            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1169        this.mNewMessage = newMessage;
1170    }
1171
1172    public int getNotificationStatus() {
1173        return mNotificationStatus;
1174    }
1175
1176    public void setNotificationStatus(int notificationStatus) throws IllegalArgumentException {
1177        if (notificationStatus < 0 || notificationStatus > 0x0001)
1178            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1179        this.mNotificationStatus = notificationStatus;
1180    }
1181
1182    public long getNotificationFilter() {
1183        return mNotificationFilter;
1184    }
1185
1186    public void setNotificationFilter(long notificationFilter) throws IllegalArgumentException {
1187        if (notificationFilter < 0 || notificationFilter > 0xFFFFFFFFL)
1188            throw new IllegalArgumentException(
1189                    "Out of range, valid range is 0x0000 to 0xFFFFFFFFL");
1190        this.mNotificationFilter = notificationFilter;
1191    }
1192
1193    public int getMasInstanceId() {
1194        return mMasInstanceId;
1195    }
1196
1197    public void setMasInstanceId(int masInstanceId) {
1198        if (masInstanceId < 0 || masInstanceId > 0x00FF)
1199            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
1200        this.mMasInstanceId = masInstanceId;
1201    }
1202
1203    public long getParameterMask() {
1204        return mParameterMask;
1205    }
1206
1207    public void setParameterMask(long parameterMask) {
1208        if (parameterMask < 0 || parameterMask > 0xFFFFFFFFL)
1209            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFFFFFF");
1210        this.mParameterMask = parameterMask;
1211    }
1212
1213    public void setConvoParameterMask(long parameterMask) {
1214        if (parameterMask < 0 || parameterMask > 0xFFFFFFFFL)
1215            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFFFFFF");
1216        this.mConvoParameterMask = parameterMask;
1217    }
1218
1219    public long getConvoParameterMask(){
1220        return mConvoParameterMask;
1221    }
1222
1223    public int getFolderListingSize() {
1224        return mFolderListingSize;
1225    }
1226
1227    public void setFolderListingSize(int folderListingSize) {
1228        if (folderListingSize < 0 || folderListingSize > 0xFFFF)
1229            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
1230        this.mFolderListingSize = folderListingSize;
1231    }
1232
1233    public int getMessageListingSize() {
1234        return mMessageListingSize;
1235    }
1236
1237    public void setMessageListingSize(int messageListingSize) {
1238        if (messageListingSize < 0 || messageListingSize > 0xFFFF)
1239            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
1240        this.mMessageListingSize = messageListingSize;
1241    }
1242
1243    public int getConvoListingSize() {
1244        return mConvoListingSize;
1245    }
1246
1247    public void setConvoListingSize(int convoListingSize) {
1248        if (convoListingSize < 0 || convoListingSize > 0xFFFF)
1249            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0xFFFF");
1250        this.mConvoListingSize = convoListingSize;
1251    }
1252    public void setSubjectLength(int subjectLength) {
1253        if (subjectLength < 0 || subjectLength > 0xFF)
1254            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x00FF");
1255        this.mSubjectLength = subjectLength;
1256    }
1257
1258    public int getCharset() {
1259        return mCharset;
1260    }
1261
1262    public void setCharset(int charset) {
1263        if (charset < 0 || charset > 0x1)
1264            throw new IllegalArgumentException("Out of range: " + charset + ", valid range is 0x0000 to 0x0001");
1265        this.mCharset = charset;
1266    }
1267
1268    public int getFractionRequest() {
1269        return mFractionRequest;
1270    }
1271
1272    public void setFractionRequest(int fractionRequest) {
1273        if (fractionRequest < 0 || fractionRequest > 0x1)
1274            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1275        this.mFractionRequest = fractionRequest;
1276    }
1277
1278    public int getFractionDeliver() {
1279        return mFractionDeliver;
1280    }
1281
1282    public void setFractionDeliver(int fractionDeliver) {
1283        if (fractionDeliver < 0 || fractionDeliver > 0x1)
1284            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1285        this.mFractionDeliver = fractionDeliver;
1286    }
1287
1288    public int getStatusIndicator() {
1289        return mStatusIndicator;
1290    }
1291
1292    public void setStatusIndicator(int statusIndicator) {
1293        if (statusIndicator < 0 || statusIndicator > 0x1)
1294            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1295        this.mStatusIndicator = statusIndicator;
1296    }
1297
1298    public int getStatusValue() {
1299        return mStatusValue;
1300    }
1301
1302    public void setStatusValue(int statusValue) {
1303        if (statusValue < 0 || statusValue > 0x1)
1304            throw new IllegalArgumentException("Out of range, valid range is 0x0000 to 0x0001");
1305        this.mStatusValue = statusValue;
1306    }
1307
1308    public long getMseTime() {
1309        return mMseTime;
1310    }
1311
1312    public String getMseTimeString() {
1313        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
1314        Date date = new Date(getMseTime());
1315        return format.format(date); // Format to YYYYMMDDTHHMMSS±hhmm UTC time ± offset
1316    }
1317
1318    public void setMseTime(long mseTime) {
1319        this.mMseTime = mseTime;
1320    }
1321
1322    public void setMseTime(String mseTime) throws ParseException {
1323        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmssZ");
1324        Date date = format.parse(mseTime);
1325        this.mMseTime = date.getTime();
1326    }
1327
1328
1329
1330
1331}
1332