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